• Необходимо перевести с английского на русский множество страниц HTML, как это можно сделать?

    Практически в каждом языке программирования есть различные средства, пакеты для решения подобных задач, автоматизации процессов - парсинга и перевода контента с различных веб сайтов.
    Если дружите с Python - для перевода googletrans – не плохо справляется. Googletrans + BeautifulSoup = готовый результат.
    Из бесплатных решений, считаю googletrans - лучшее.
    Ответ написан
    Комментировать
  • Vue.js + Fancybox, как использовать совместно?

    @voland700 Автор вопроса
    После долгих танцев с бубном проблема была решена. Опишу, возможно кому-нибудь пригодится.

    Fancybox v. 5 отлично работает, в том числе в связке с Vue.js. На сайте использовалась верстка, css - от существующего проекта, на котором так же использовалась Fancybox, предыдущей, 4 версии библиотеки. После удаления из css - файла фрагментов ранее используемого css Fancybox - проблема решена.
    Ответ написан
    Комментировать
  • Как создать кастомную страницу для создания записи в Laravel Moonshine?

    Не много не понял Ваших целей в создании на кастомных страниц списка категорий и создания модели. А станица редактирования данных тоже будет на кастомной? Или Вы хотите что бы страница списка была в виде дерева, с вложенностью?

    Я использую Moonshine, в принципе можно сделать как Вы хотите, сделать кастомную страницу, на которой буде размещена форма для создания модели. Документация к Moonshine довольно подробная, и там хорошо, с примерами изложена информация о создании кастомных страниц.

    К примеру , используя кастомные страницы Moonshin создавал функционал экспорта, импорта данных, обновления курсов валют. Возможно на данном примере Вы сможете решить свою задачу.

    1. В app\Providers\MoonShineServiceProvider.php создаем пункт меню, ведущий на кастомную страницу админ панели.
    MenuItem::make('Импорт товаров',
                        CustomPage::make('Excel import', 'product-import', 'admin.import.import_product', fn() => [])
                    )->icon('heroicons.document-arrow-down'),

    Тут можно увидеть параметры:
    • 'Excel import',< - заголовок кастомной страницы.
    • 'product-import' - slug страницы для формирования url.
    • 'admin.import.import_product', - blade шаблон view кастомной страницы, который как обычно располагается в resources/views.
    • fn() => [] Функция cfllback – в данном случае пустая, так как нет необходимости в обработке и передаче данных во view.

    2. Создаем blade шаблон кастомной страницы. В моем случае он находился resources/views/'admin/import/import_product.blade.php. В данном шаблоне вы можете создать форму для создания элемента. Можно накидать свою, а можно использовать готовые UI компоненты Moonshine
    Пример blade шаблон кастомной страницы.

    @if (session('status'))
        <x-moonshine::alert type="success" removable="true">{{ session('status') }}</x-moonshine::alert>
    @endif
    @if (isset($errors) && $errors->any())
        <x-moonshine::alert type="error">
            @foreach ($errors->all() as $error)
                {{ $error }}
            @endforeach
        </x-moonshine::alert>
    @endif
    <form action="{{route('import.product')}}" method="post" enctype="multipart/form-data">
        @csrf
        <div class="grid grid-cols-12 gap-6">
            <div class="space-y-6 col-span-12 xl:col-span-6">
                <div class="box">
                    <h2 class="box-title">Импорт данных товаров из EXCEL - файла</h2>
                    <div class="grid grid-cols-12 gap-6">
                        <div class="form-group space-y-6 col-span-12 xl:col-span-9" x-show="true" id="wrapper_">
                            <label class="form-label" for="file">
                                Excel файл
                            </label>
                            <div class="form-group form-group-dropzone">
                                <input class="form-input form-file-upload" type="file" name="file" accept="*/*">
                            </div>
                        </div>
                    </div>
                </div>
            </div>
        </div>
    
        <div class="space-y-6 col-span-12 xl:col-span-12">
            <div class="mt-3 flex w-full flex-wrap justify-start gap-2">
                <button type="submit" class="btn btn-primary form_submit_button">Отправить</button>
            </div>
        </div>
    </form>


    2. Action – ссылка формы ведет на контроллер, точнее метод обработчик – создания модели. Создаем роут для ссылки
    Route::post('/moonshine/product-import', [App\Http\Controllers\Admin\Import\ImportController::class, 'importProduct'])->name('import.product');

    4. Создать конроллер и соответствующий метод для валидации и сохранения данных из формы.


    А вообще MoonShine имеет довольно большое и дружелюбное комьюнити, где сами разработчики и опытные пользователи быстро помогаю решать различные задачи, дают советы и отвечаю на вопросы. Тут можно получить ответы на вопросы связанные с MoonShine - t.me/laravel_chat

    P.S
    Ответ написан
    Комментировать
  • Ошибка Network error – при загрузки картинок на Bitrix. Как поправить?

    @voland700 Автор вопроса
    Проблема решена. Поделюсь решением, возможно кому-нибудь пригодится.
    Обнаружил в /bitrix/tools/ - .htaccess файл, содержимое:
    <Files ~ "^(html_editor_action|mail_entry|upload)\.php$>
    deny from all
    </Files>


    Удалил файл /bitrix/tools/.htaccess и все заработало.

    В чистой, оригинальной версии БУС, этого файла нет. Полагаю, был данный файл был добавлен предшественником, для усиления безопасности Битрикс от взломов. Однако вместе с этим был нарушен функционал загрузки картинок через форму создания и редактирования элементов инфоблока.
    Ответ написан
    Комментировать
  • Как правильно спарсить характеристики с сайта и записать в отдельные колонки эксель?

    Недавно решал подобную задачу, при парсинге характеристик товаров, получал результирующий файл в Excel, где помимо общих данных товара: наименование, цена, описания, картинки и т.д., все характеристики были разнесены по разным колонкам, как то так:

    636cb8986b6f6371909319.jpeg
    Для получения данных с сайта источника, тоже использовал библиотеку BeautifulSoup,

    Фрагмент HTML кода с характеристиками товара на сайте источнике для парсинга:
    <div id="content_features" class="ty-wysiwyg-content content-features">
    	<div class="ty-product-feature">
    		<div class="ty-product-feature__label">Ширина:</div>
    		<div class="ty-product-feature__value">1400<span class="ty-product-feature__suffix"> мм</span></div>
    	</div>
    	<div class="ty-product-feature">
    		<div class="ty-product-feature__label">Высота:</div>
    		<div class="ty-product-feature__value">1345<span class="ty-product-feature__suffix"> мм</span></div>
    	</div>
    	<div class="ty-product-feature">
    		<div class="ty-product-feature__label">Глубина:</div>
    		<div class="ty-product-feature__value">880<span class="ty-product-feature__suffix"> мм</span></div>
    	</div>	
    </div>

    Фрагмент кода получения характеристик товара:
    propsBody = []
            try:
                propsBody = soup.find('div', {'id' : 'content_features'}).findAll('div', class_='ty-product-feature')
            except:
                pass
            if len(propsBody) > 0:
                #self.properties.clear()
                propsItems = []
                for props in propsBody:
                    item = {
                        'label' : props.find('div', class_='ty-product-feature__label').get_text(strip=True),
                        'value' : props.find('div', class_='ty-product-feature__value').get_text(strip=True)
                    }
                    propsItems.append(item)
                self.properties = propsItems

    В результате, полученные данные характеристик, для каждого товара в виде словаря, типа:
    [{"label": "Высота:", "value": "220мм"}, 
    {"label": "Глубина:", "value": "295мм"}, 
    {"label": "Материал:", "value": "Сталь"},
    {"label": "Вес:", "value": "4,3кг"},
    {"label": "Длина:", "value": "650мм"}]

    Где в label - название характеристики, а в value – значение.

    Учитывая, что у всех товаров разные типы, названия характеристик, собирал их в множество – так как в множестве могут быть только уникальные значения. Затем, при записи данных в Excel, как предлагал выше shurshur, автоматически добавлял колонки из полученных данных.

    Вот пример класса, который принимает полученные в результате парсинга данные товаров, и записывает их Excel.
    В данном прмере Вы можете увидеть, что помимо общих полей – для данных товара, как название, цена, описание и т.д, из названий характеристик в цикле добавляются колонки, куда записываются значения характеристик товара:

    spoiler

    class ExcelFile:
        data = {}
        labels = set() # множество, перменная, свойство для названий характеристик товаров
    
        def __init__(self, data):
            self.data = data 
    
        def get(self):
            # В цикле проходим по данным полученным в результате парсинга,
            # и собираем названия характеристик в множество
            for item in self.data:
                if len(item['properties']) > 0:
                    for prop in item['properties']:
                        self.labels.add(prop['label'])
    
            book = openpyxl.Workbook()
            sheet = book.active
            
            # Поля, колонки Excel - файла, для разных данных товаров
            sheet['A1'].value = 'src'
            sheet['B1'].value = 'name'
            sheet['C1'].value = 'category'
            sheet['D1'].value = 'art_namber'
            sheet['E1'].value = 'price'
            sheet['F1'].value = 'main'
            sheet['G1'].value = 'more'
            sheet['H1'].value = 'descrip'
            sheet['I1'].value = 'JSON_properties'
            sheet['J1'].value = 'video'
            sheet['K1'].value = 'docs'
            # Добавляем дополнительные колонки в Excel из названий характеристик
            if len(self.labels) > 0:
                nam = 12
                for nameProp in self.labels:
                    sheet.cell(row=1, column=nam, value=nameProp)
                    nam += 1
            row = 2
            # Записываем в строки Excel- файла данные товаров
            for res in self.data:
                sheet[row][0].value = res['src']
                sheet[row][1].value = res['name']
                sheet[row][2].value = res['category']
                sheet[row][3].value = res['art_namber']
                sheet[row][4].value = res['price']
                sheet[row][5].value = res['main']
                sheet[row][6].value = ','.join(res['more'])
                sheet[row][7].value = res['descrip']
                sheet[row][8].value = res['json']
                sheet[row][9].value = ','.join(res['video'])
                sheet[row][10].value = ','.join(res['docs'])
                # Записываем значения характеристик товара в соответствующие названиям колонки Excel- файла
                if len(self.labels) > 0:
                    namberCell = 12
                    while namberCell < len(self.labels) + 12:
                        propLabel = sheet.cell(row=1, column=namberCell).value
                        for property in res['properties']:
                            if property['label'] == propLabel:
                                sheet.cell(row=row, column=namberCell).value = property['value']
                            else:
                                sheet.cell(row=row, column=namberCell)
                        namberCell += 1
                row += 1
    
            file_name = detNameExcelFile()
            book.save(file_name)
            book.close()
    
            return file_name



    Возможно, данную задачу можно решить другим, более рациональным способом, но тем не менее -это способ работает.
    Ответ написан
    2 комментария
  • Как запустить бекап сайта с одного на другой сервер на Laravel?

    Ну если хосты настроены правильно, то после выполнения этих действий по переносу сайта на Laravel на новый хостинг, все должно работать:

    1. Делаем архив файлов и дамп базы.
    2. Переносим файлы на новый сервер и создаем базу данных
    3. Экспортируем данные в новую базу данных из дампа
    4. Вносим необходимые правки в файл .ENV:

    APP_NAME=your-site.ru
    APP_DEBUG=true
    APP_URL=http:// your-site.ru

    DB_DATABASE=db-name
    DB_USERNAME=user-nqme
    DB_PASSWORD=password


    5. Удаляем закэшировнные настройки старого сервера: переименовываем файл /bootstrap/cache/config.php на другое имя, к примеру: config_xxx.php. Новый файл config.php с новыми настройками будет создан при запуске сайта.
    6. Очищаем кэш:
    php artisan config:cache
    php artisan cache:clear
    php artisan route:cache
    php artisan view:clear
    php artisan optimize
    Ответ написан
  • После переполнения диска на хостинге полетел MySql – повреждена база данных, как восстановить?

    @voland700 Автор вопроса
    Проблема решена путем восстановления MySql баз данных из файлов с перезапуском mysql c опцией innodb_force_recovery.
    Если коротко, то процесс восстановления InnoDB базы данных происходит так :

    1. Останавливаем mysql сервер.
    2. В файл /root/my.cnf вносим параметр innodb_force_recovery = 1
    3. Запускаем mysql сервер
    4. В случае успешного запуска сервера, создаем дамп баз данных;
    5. Удаляем из файла /root/my.cnf вносим параметр innodb_force_recovery
    6. Удаляем поврежденную базу данных
    7. Перезапускаем mysql сервер.
    8. Восстанавливаем базу данных из полученного дампа

    Боле детально, с подробными пояснениями и примерами можно посмотреть тут: восстановление MySql базы данных.
    Ответ написан
    Комментировать
  • Сайт открывается на чужом домене, совпадают IP адреса, как настроить Ahache правильно?

    @voland700 Автор вопроса
    Все, проблема решена. Возможно, кто-то столкнётся с такой проблемой, по этому опишу решение.

    В конфигурационные файлы Apache для домена добавил условие:
    UseCanonicalName Off
       <If "tolower(%{SERVER_NAME}) != 'my-site.ru'">
          AllowOverride None
          Require all denied
       </If>

    В результате заблокировал всех пользователей, которые пытаются получить доступ к веб-сайту с другим доменным имением. Теперь при переходе на сайт по другому, не принадлежащем мне домену, пользователь получает ошибку:

    er-403-img.jpg
    Если Вы знаете другое, лучшее решение, опишите пожалуйста.
    Ответ написан
    Комментировать
  • Как узнать количество товаров в битриксе?

    Для примера, так получаем количество активных элементов, товаров каталога,
    <?
    //если если ранее по коду модуль инфоблока не подключен, то подключаем его
    CModule::IncludeModule("iblock"); 
    
    $iblock_id = 3;   // Цифровое значение ID - Вашего Инфоблока - каталога товаров
    $arFilter = Array("IBLOCK_ID"=>$iblock_id, "ACTIVE"=>"Y");
    $count_goods = CIBlockElement::GetList(Array(), $arFilter, Array(), false, Array());
    ?>
    Количество товаров: <? echo $count_goods; ?>

    В переменной $count_goods - количество товаров, элементов каталога.
    CIBlockElement::GetList - подробней тут.
    Ответ написан
    Комментировать
  • Вредоносный код на сайте?

    На самом деле проблема актуальна и широко обсуждается на форуме Bitrix в ветке: Взломаны сайты в честь дня конституции Украины. Рекомендую ознакомится, много информации о причинах взлома, и способах решения проблемы.

    Но без обновления БУС решить проблему не получится, так как, без обновления, после удаления вредоносного кода уязвимость остается, и проблема повторяется.
    Ответ написан
    Комментировать
  • После установки SSL сертификата и перехода на HTTPS – ошибка: Сайт выполнил переадресацию слишком много раз. Как исправить?

    @voland700 Автор вопроса
    Все! Проблема решена. Возможно информация кому-нибудь будет полезна.
    Проблема была в особенностях хостинга где арендован VDS сервер, - Джино, они по умолчанию предоставляют включенным проксирование, перенаправление портов, якобы для удобства пользователей. Которое можно просто отключить в настройках.
    Ответ написан
    Комментировать
  • Как наиболее рационально обновлять данные в моделях Laravel?

    @voland700 Автор вопроса
    Благодарю Всех за помощь и информацию в решении задачи.

    Учитывая, что данная информация возможно будет полезна для других, опишу выбранный мною способ решения.
    Действительно, задачу регулярного обновления цен в зависимости от курса валют, можно решить разными способами. Для себя я выбрал способ обновления данных с использованием сырых raw запросов к базе данных. Учитывая, что данные курса валют обновляются не часто, только раз в сутки – считаю это будет оптимальным, не ресурсозатратным способом.

    Данные товаров, в том числе их стоимости, хранятся в таблице ‘ product’, в которой так же содержаться поля для определения цены товара:
    1. ‘base_price’ – Базовая цена товара, стоимость в рублях, долларах, евро
    2. ‘currency’ – Тип, в какой валюте задана базовая цена: в рублях RUB, в евро EUR или долларах USD
    3. ‘price’ – конвертированная цена, с учетом текущего курса валюты.

    5ff458415c119314189916.jpeg

    Свежие данные о курсе валют получаются по API Центрбанка РФ и сохраняются в таблице ‘currency’.
    5ff4585ae2d92902315016.jpeg
    В контроллере получаю коллекцию и преобразую в массив с необходимыми данными о типе валюты, номинале и значении действующего курса.
    $currency = Currency::select('currency', 'Nominal', 'value')->get()->keyBy('currency')->toArray();


    Затем с помощью RAW запросов обращаюсь к базе для обновления каждого типа валюты. На пример, для товаров, базовая цена которых задана в ЕВРО (‘currency’ = 'EUR'), обновление данных будет происходить следующим запросом:
    $countUpdate = DB::update("UPDATE `product` SET `price` = (`base_price` * ?)  WHERE `currency`='EUR'", [ $currency['EUR']['value'] ]);


    То есть из таблицы товаров ("UPDATE `product`) выбираются данные товаров где поле ‘currency’ – тип валюты, задано как (WHERE `currency`='EUR'), и для поля `price` устанавливаются значения, где данные базовой цены умножаются на сумму действующего курса валюты (`price` = (`base_price` * $currency['EUR']['value']).

    Таким образом, четырьмя аналогичными запросами происходит конвертация в рубли и обновление цен товаров, цена которых задана в валюте. Используя функции MySQL ELT() и FIELD() данные запросы можно объединить в один.
    Ответ написан
    Комментировать
  • Laravel Nestedset - дерево категорий, проблема с обновлением данных. Как исправить?

    @voland700 Автор вопроса
    Проблема решена, опишу решение.
    1. В моем случае была ошибка в логике. Пытался ID - редактируемой категории сохранить как parent_id, в результате получал сообщение Kalnoy\Nestedset\NodeTrait об ошибке, что категория не может быть вложена сама в себя. Ошибка возникла при копировании формы для создания категории в шаблон для редактирования данных.
    2. Просто присвоить parent_id - данные id родительской категории, для построения Kalnoy\Nestedset\NodeTrait дерева вложенных категорий недостаточно. Для пересчета дерева, если новой категории напрямую присвоить parent_id, необходимо запустить метод:
    $category::fixTree();
    данный метод запускает перерасчет дерева на основе данных parent_id, заполняет в таблице значения полей _lft и _rgt, на основе которых строится дерево вложенности.
    Ответ написан
    Комментировать
  • Какую панель выбрать для хостинга?

    Пользуюсь BrainyCP - бесплатная и функциональная серверная хостинг-панель. Ставлю на VDS, весь необходимый функционал в наличии.
    Ответ написан
    Комментировать
  • Ошибка 419 после переноса сайта на Laravel на хостинг. Как исправить?

    @voland700 Автор вопроса
    Проблема решена. В моем случае проблема была в конфигурации сервера. Сайт размещен на VDS сервере с панелью управления BrainyCP. Панель была сконфигурирована для работы сайтов на Битрикс, в том числе необходимый параметр для работы Битрикс - BrainyCP mbstring.func_overload.
    Согласно логам сервера - при попытке сохранить какие либо данные через форму на сайте отрабатывала 419 ошибка.
    В логах сервера запись о данной ошибке в виде:
    [Fri Nov 13 12:35:33.249913 2020] [proxy_fcgi:error] [pid 18854:tid 140666176743168] [client 212.45.19.58:43938] AH01071: Got error 'PHP message: PHP Deprecated:  The mbstring.func_overload directive is deprecated in Unknown on line 0', referer: http://hight-control.ru/

    Что говорит о проблемах с настройкой PHP mbstring.func_overload.

    Проблема была решена установкой на сервере новой версии PHP 7.4 с параметрами по умолчанию, и переключению сайта на работу на данной версии PHP.
    Ответ написан
    Комментировать
  • После переноса приложения на Laravel на сервер приложение не видит контроллеры. Как исправить?

    @voland700 Автор вопроса
    Проблема устранена. Причина имя файла контроллера было с маленькой буквы InfoController.php, название класса указано с большой
    class InfoController extends Controller
    	{
    		//
    	}

    На локальном сервер под Windows все работало, но на перенеосе на хостинг проблема появилась.
    Ответ написан
    Комментировать
  • Как удалить несколько файлов при одновременном удалении поста и информации в базе данных?

    @voland700 Автор вопроса
    Опишу решение данной задачи. Мною задача по одновременному удалению файлов галерей при удалении поста, решена так:
    //Pechnik -  модель/таблица содержит данные поста
    //Images - модель/таблица содержит информацию о  изображениях галереи 
     
    // - метод ресурсного контролера для удаления поста
        public function destroy($id)
        {
            $images = Images::where('pechnik_id', $id); //получаю колекцию файлов галереи превязанных к ID поста p из таблицы Images
            $pathImages = $images->get('img'); //получаю данные наименование и путь файлов галереи превязанных к ID поста
            if (!$pathImages->isEmpty()){ 
                foreach ($pathImages as $img){ 
                    $path = $img->img; 
                    Storage::disk('public')->delete(str_replace('storage', '', $path)); //в цикле удаляю фалы галереи превязанных к ID поста
                }
            }
            $images->delete(); // удаляю записи в таблице Images:: о файлах галереи
            $pechnik= Pechnik::find($id);   // получаю данные поста по ID из таблицы Pechnik::
            Pechnik::find($id)->delete();  // удаляю запись в таблице Pechnik:: - данные поста
            return redirect()->route('admin.index')->with('success', 'Информаця успешно удалена');
        }
    Ответ написан
    Комментировать
  • Как получить список товаров каталога по ID, с данными о ценах и размерах скидки?

    @voland700 Автор вопроса
    Задача решена. Опишу решение, возможно кому-нибудь пригодится.

    Изначально, необходимо было вывести на главной странице каталога блок с товарами каталога, по свойству PROPERTY_STICKERS – типа список, которым заданы значения Хит, Советуем, Новинки, Акции для вывода товаров с соответствующими стикерами. При этом товары должны выводится с размером скидок, указанием старой цены, ценой конвертированной из Валюты в рубли.

    5ef994cd5227f620637405.jpeg

    Методы битрикс CIBlockElement::GetList и CCatalogDiscount::GetList не идеальны для решения данной задачи. По моему мнению целесообразней использовать компонент catalog.section с фильтром по свойству. Для чего в параметрах компонента задан фильтр:
    "USE_FILTER" => "Y",
         "FILTER_NAME" => "arrFilterAdvice",


    И для получения всех товаров каталога, в которых заданы значения свойству PROPERTY_STICKERS (свойство не пустое) задан фильтр.

    <? $GLOBALS['arrFilterAdvice'] = array('ACTIVE' => 'Y', '!PROPERTY_STICKERS' => false);
    $APPLICATION->IncludeComponent(
    	"bitrix:catalog.section", 
    	"shop_window", 
    	array(
              …
              "USE_FILTER" => "Y",
              "FILTER_NAME" => "arrFilterAdvice", 
              …


    Таким образом получен список товаров которым заданы значения, и уже в файле result_modifier.php сортирую полученные элементы по массивам - Хит, Советуем, Новинки, Акции, добавляю в результатирующий массив $arResult для кеширования.

    if(!$arResult["ADVICE"]){
    	foreach($arResult["ITEMS"] as $Element){
    		foreach($Element["PROPERTIES"]["STICKERS"]["VALUE_XML_ID"] as $type){		
    			if($type == "HIT") $arResult["ADVICE"]["HIT"][$Element["ID"]] = $Element;
    			if($type == "RECOMMEND") $arResult["ADVICE"]["RECOMMEND"][$Element["ID"]] = $Element;
    			if($type == "NEW") $arResult["ADVICE"]["NEW"][$Element["ID"]] = $Element;
    			if($type == "STOCK") $arResult["ADVICE"]["STOCK"][$Element["ID"]] = $Element;
    		}
    	}
    }


    Полученные данные вывожу в шаблоне компонента catalog.section в виде табов.
    Ответ написан
    Комментировать
  • Почему не работает фильтр для выборки элементов инфоблока по ID?

    @voland700 Автор вопроса
    Проблема решена - способы решения находятся в комментариях.
    Суть проблемы в том, что данные содержащиеся в $arResult в файле component.epilog.php недоступны. Для передачи необходимых параметров содержащихся $arResult необходимо их пробросить из кешируемой области компонента в component_epilog.php:
    $cp = $this->__component;
    if (is_object($cp))
    {
      $cp->arResult["ASSOCIATED"] = $arResult["PROPERTIES"]["ASSOCIATED"]["VALUE"];
      $cp->SetResultCacheKeys(array("ASSOCIATED")); //cache keys in $arResult array
    }


    Либо передавать через $arParams параметры настроек компонента:
    $templateData["ASSOCIATED"] = $arResult["PROPERTIES"]["ASSOCIATED"]["VALUE"];
    Ответ написан
    Комментировать