ADFM
Общая информация
ADFM - система управления контентом, написанная специально для разработчиков студии Вебтолк.
Система выполнена в виде пакета Laravel wtolk/adfm .
Основные задачи которые решает система ADFM:
быстрая разработка типовых сайтов
Легкая верстка шаблонов
Простота изучения большинством программистов понимающих PHP, MVC паттерн, и основы ООП.
Установка
Текущую версию дистрибутива можно скачать здесь:http://adfm.wtolk.ru/adfm.zip
Для корректной работы необходима версия PHP >= 7.4 Для простой и быстрой установки, необходимо скачать фаил установщика из репозитория https://github.com/wtolk/adfm_installer/blob/master/dist/adfm_installer.phar и создать конфиг по примеру https://github.com/wtolk/adfm_installer/blob/master/dist/config.yaml
и запустить его командой
php adfm_installer.phar
Данный установщик скачает свежую версию Laravel в папку {{config:path}}, установит пакеты {{ config:packages }} и задаст конфиг подключения к базе {{ config:db }}
Ручная установка
Устанавливаем свежий ларавел composer create-project laravel/laravel
Устанавливаем пакет с Adfm composer require wtolk/adfm --no-cache
Вспомогательные пакеты
composer require barryvdh/laravel-debugbarcomposer require barryvdh/laravel-ide-helper
Публикуем файлы пакетов php artisan vendor:publishПрименяем миграции php artisan migrate Создаем root пользователя
php artisan adfm:user test test@test.ru test
Файлы шаблонов
Существуют следующие страницы шаблонов :
Общий шаблон - layout.blade.phpГлавная страница - index.blade.phpОбычная страница - page.blade.phpФункции и переменные используемые в шаблонах
Вывести все переменные, передаваемые в шаблон :
{{ dd(get_defined_vars()) }}
Переменные Page
{{$page->title}} - название страницы
{!! $page->content !!} - текст из визуального редактора
{{$page->files}} - прикрепленные файлы через поле MultiUpload
Пример вывода файлов
@if(count($page->files) > 0)
@endif
Вывести меню в шаблоне :
@php($links = \App\Models\Adfm\Menu::getData('main-menu'))
@foreach($links[0] as $el)
{{$el->title}}
@endforeach
Идентификатор меню можно посмотреть в админке в разделе меню (Выделено красным) :
Пример из реальной жизни :
(УСТАРЕЛО) Переменные передаваемые в шаблон
Общая информация Переменные, которые передаются в шаблон, можно посомтреть с помощью выражения{% autoescape false %} {{dump()}} {% endautoescape %}
Переменные Page
{{page.id}} => Уникальный идентификатор страницы{{page.title}} => Заголовок страницы{{page.alias}} => Синоним страницы{{page.content}} => html контент страницы, созданный через WYSISWYG редактор текста{{page.create_date}} => Время создания страницы в UNIX-времени{{page.type}} => идентификатор типа страницы (0 - обычная страницы, 1 - новость, 2 галерея){{page.metaTitle}} => Метатег title - генерируется из заголовка страницы. В большинстве случаев они совпадают{{page.metaDescription}} => Метатег description - генерируется из первых символов контента страницы. Хотя может быть задан отдельно.(УСТАРЕЛО) Модуль каталог товаров
С помощью данного модуля можно создавать страницы товаров, и организовывать их по категориям.Лежит в папке /app/modules/product_catalog.
Адреса и шаблоны страниц
/categories - страница всех категорий (category-list-page.html.twig)
/category/{id} - страница одной категории (category-page.html.twig)
/product/{id} - страница товара (product-page.html.twig)
твиг функции
getCategories() - возвращает список категорий верхнего уровня. Пример использования :
{% set categories = getCategories() %}
{% for category in categories %}
{{ category.title }}
{% for item in category.children %} # дочерние категории
{{ item.title }}
{% endfor %}
{% endfor %}
{% set categories = getCategories() %}
{% for category in categories %}
{{ category.title }}
{% for item in category.children %} # дочерние категории
{{ item.title }}
{% endfor %}
{% endfor %}
свойства моделей
Категория
{{category.id}} - идентификатор категории
{{category.parent_id}} - идентификатор родительской категории
{{category.position}} - порядковый номер в списке. Этот параметр нужен, для сортировки категорий в таком порядке, как они заданы в админке
{{category.title}} - имя категории
{{category.children}} - дочерние подкатегории.
{{category.parent}} - родительская категория.
{{category.image}} - Картинка категории. Для вывода пути картинки написать {{category.image.url}}
{{category.created_at}} - дата создания категории
{{category.deleted_at}} - дата удаления категории
товар
{{product.id}} - идентификатор товара
{{product.article}} - артикул товара (необязателен для заполнения). Нужен для синхронизации с 1с или прайсом
{{product.title}} - название товара
{{product.alias}} - синоним товара
{{product.description }} - описание товара. Что бы вывести описание без тегов пишем {{product.description|raw }}
{{product.price}} - цена товара.
{{product.image}} - Картинка товара. Для вывода пути картинки написать {{product.image.url}}
{{product.categories}} - Массив категорий товара. Для вывода всех категорий пишем {% for category in product.categories %}
{{product.created_at}} - дата создания товара
{{product.updated_at}} - дата обновления товара
(УСТАРЕЛО) модуль Новости
Список новостей и специальный блок для вывода анонсовЛежит в папке /app/modules/news.
Адреса и шаблоны страниц
/news - страница всех новостей (news.html.twig)
/news/{id} - страница одной новости (news-page.html.twig)
твиг функции
getLatestNews($count=4) - возвращает список последних новостей (по умолчанию 4). Пример использования :
{% set news = getLatestNews(9) %} # 9 последних новостей
{% for item in news %}
{{ item.title }}
{% endfor %} (УСТАРЕЛО) Обратная связь
Модуль для формы обратной связи . Для корректной работы, нужно задать правильные настройки в файле /app/config/mail.yamlЛежит в папке /app/modules/contactform.
Адреса и шаблоны страниц
/api/feedback/send - на этот адрес нужно отправить результаты формы. Можно ajax
Шаблон формы находится в файле form.html
твиг функции
getContactForm() - рендерит форму обратной связи в этом месте
{{ getContactForm() }}
свойства моделей
Новость
{{news.id}} - идентификатор новости
{{news.title}} - имя новости
{{news.content}} - Полный текст новости. Что бы вывести описание без тегов пишем {{news.content|raw}}
{{news.image}} - Картинка новости. Для вывода пути картинки написать {{news.image.url}}
{{news.created_at}} - дата создания новости
{{news.updated_at}} - дата обновления новости
Основы
Система состоит из нескольких пакетов Laravel:https://github.com/wtolk/adfm - Содержит общие классы , контроллеры и так далее https://github.com/wtolk/crud - Содержит классы для работы с админкой и генератор моделей, контроллеров и экранов из таблицы бд.
Посмотреть админку можно по адресу /admin/pages . Далее нужно будет ввести логин и пароль заранее созданного пользователя. Как создать пользователя, можно посомтреть на странице с информацией о установке Экран админки
form = new FormPresenter();
$this->request = request();
}
public static function index()
{
$screen = new self();
$screen->form->template('table-list'); // Объявляем шаблон страницы с таблицей
$screen->form->source([
'pages' => Page::filter(request()->input('filter'))->paginate(50) // Задаем модели для экрана
]);
$screen->form->title = 'Страницы'; // Заголовок экрана
$screen->form->addField(
TableField::make('title', 'Название страницы')
->link(function ($model) {
echo Link::make($model->ru_title)->route('adfm.pages.edit', ['id' => $model->id])
->render();
})
);
$screen->form->addField(TableField::make('created_at', 'Дата создания'));
$screen->form->addField(
TableField::make('', '')
->link(function ($model) {
echo Link::make('Удалить')->route('adfm.pages.destroy', ['id' => $model->id])->render();
})
);
$screen->form->addField(
TableField::make('', '')
->link(function ($model) {
echo Link::make('Просмотр')->route('adfm.show.page', ['slug' => $model->slug])->render();
})
);
$screen->form->filters(self::getFilters()); // Объявляем поля по которым будем фильтровать таблицу
$screen->form->buttons([ // Кнопки на экране
Link::make('Добавить')->class('button')->icon('note')->route('adfm.pages.create')
]);
$screen->form->build();
$screen->form->render();
}
public static function create()
{
$screen = new self();
$screen->form->isModelExists = false;
$screen->form->template('form-edit')->source([
'page' => new Page()
]);
$screen->form->title = 'Создание страницы';
$screen->form->route = route('adfm.pages.store');
$screen->form->columns = self::getFields(); // В свойство поля передаем метод, в котором возвращаем список полей
$screen->form->buttons([
Button::make('Сохранить')->icon('save')->route('adfm.pages.update')->submit(),
]);
$screen->form->build();
$screen->form->render();
}
public static function edit()
{
$screen = new self();
$screen->form->isModelExists = true;
$screen->form->template('form-edit')->source([
'page' => Page::findOrFail($screen->request->route('id'))
]);
$screen->form->title = 'Редактирование страницы';
$screen->form->route = route('adfm.pages.update', $screen->form->source['page']->id);
$screen->form->columns = self::getFields();
$screen->form->buttons([
Button::make('Сохранить')->icon('save')->route('adfm.pages.update')->submit(),
Button::make('Удалить')->icon('trash')->route('adfm.pages.destroy')->canSee($screen->form->isModelExists),
Link::make('Добавить в меню')->icon('trash')
->route('adfm.menuitems.createFromModel', [
'model_name' => 'Page',
'model_id' => $screen->request->route('id'),
'menu_id' => '0',
])->canSee($screen->form->isModelExists)
]);
$screen->form->build();
$screen->form->render();
}
public static function getFilters() {
return [
Input::make('filter.title:like')->title('Заголовок страницы')->setFilter(),
Input::make('filter.content:like')->title('Текст страницы')->setFilter(),
];
}
public static function getFields() {
return [
Column::make([
Input::make('page.ru_title')
->title('Заголовок на русском')
->required(),
Input::make('page.title')
->title('Заголовок на английском')
->required(),
Summernote::make('page.ru_content')->title('Содержимое на русском')->devMode($dev_mode),
Summernote::make('page.content')->title('Содержимое на английском')->devMode($dev_mode),
MultiFile::make('page.files')->title('Прикрепленные документы')
]),
Column::make([
Input::make('page.slug')
->title('Вид в адресной строке'),
Input::make('page.meta.title')
->title('TITLE (мета-тег)'),
Input::make('page.meta.description')
->title('Description (мета-тег)'),
])->class('col col-md-4')
];
}
}
ImageCache
Вспомогательный класс, который делает копии изображений заданного размера, что бы гарантировать что изображение которое загрузит пользователь будет соответствовать необходимым размерам.
Как пользоваться :
{!! ImageCache::get($file, ['w' => 300, 'h' => 200, 'fit' => 'crop']) !!}
Где, $file - объект типа App\Models\Adfm\File'w' => ширина фото,'h' => высота фото,'fit' => тип обрезки изображения, доступные значения :
crop-top-left, crop-top, crop-top-right, crop-left, crop-center, crop-right, crop-bottom-left, crop-bottom crop-bottom-right Значение crop это то же самое что и crop-centerДанное объявление вернет тег
с адресом на обрезанную картинку. Для картинки можно задать следующие атрибуты title, alt, class, id с помощью соответсвующих методов :
{!! ImageCache::get($file, ['w' => 300, 'h' => 200, 'fit' => 'crop'])->title('заголовок') !!}
{!! ImageCache::get($file, ['w' => 300, 'h' => 200, 'fit' => 'crop'])->alt('альтернативная подпись') !!}
{!! ImageCache::get($file, ['w' => 300, 'h' => 200, 'fit' => 'crop'])->className('img-fluid') !!}
{!! ImageCache::get($file, ['w' => 300, 'h' => 200, 'fit' => 'crop'])->id('logo') !!}
Примеры из реальной жизни :
Выводим товары на странице категории
@foreach($products as $product)
@if($product->images[0])
{!! ImageCache::get($product->images[0], ['w' => 265, 'h' => 265, 'fit' => 'crop']) !!}
@endif
{{$product->title}}
{{$product->price}} ₽
@endforeach
на строке 3 проверяем, есть ли у товара хотя бы одно изображение, если да, то показываем обрезанную копию.