Создание бандла на symfony 5. Часть 2. Основа бандла.

Опубликовано admin - пт, 05/29/2020 - 21:26

Установка Symfony 5. Установка своего бандла.Итак, в предыдущей статье наш бандл установлен. Давайте создадим его первый роутер /newtop/.
Прежде чем Вы перейдете по этому адресу, не забудьте добавить в файл myapp\config\routes\annotations.yaml

newstop_bundle:
    resource: '@NewsTopBundle/config/routes.yaml'

Мы напишем бандл под названием NewsTop, который будет выводить новости с обновлением данных через ajax.
NewsTop – приложение для вывода новостей на сайте.
Все новости буду иметь изображение, поэтому для работы с изображениями нам понадобится прекрасная библиотека «liip», установить которую можно следующим образом:

composer require liip/imagine-bundle

итак создадим путь и файл myapp/bundles/NewsTopBundle/config/routes.yaml следующего содержания:

# Add this file to app routes.yaml to use routes  custom loader.
newstop_routes:
    type: annotation
    resource: '../src/Controller/'

Обратите внимание маршруты будут строится на основе механизма аннотаций «type: annotation».
В директории Controller необходимо создать следующие классы:
•    myapp/bundles/NewsTopBundle/src/Controller/NewsTopController.php
•    myapp/bundles/NewsTopBundle/src/Controller/NewsTopEditorController.php
•    myapp/bundles/NewsTopBundle/src/Controller/NewsTopAjaxController.php
•    NewsTopController.php –класс для работы с бандлом.
•    NewsTopEditorController.php –класс для вывода списка новостей и редактирования сущностей.
•    NewsTopAjaxController.php – основной класс для обработки ajax запросов.
Весь код я не буду размещать в статье, код бандла Вы сможете обнаружить на github.com по ссылке https://github.com/eap1985/newstop
Я остановлюсь на тех частях, которые наиболее сложны в понимании.
Новости будут строится на основе сущности (entity), для этого нам нужно создать файл по адресу: myapp/bundles/NewsTopBundle/src/Entity/NewsTop.php
 

<?php

namespace eap1985\NewsTopBundle\Entity;

use DateTime;
use DateTimeInterface;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Context\ExecutionContextInterface;
use Vich\UploaderBundle\Mapping\Annotation as Vich;
use Symfony\Component\Validator\Constraints as Assert;

/**
 * @ORM\Table(name="newstop")
 * @ORM\Entity
 * @Vich\Uploadable()
 * @Assert\Callback("validate")
 */
class NewsTop
{

//здесь будет код ORM сущности, свойства и методы

}

Сущность ORM привязана к таблице базы данных newstop, тестовая структура которой следующая:

CREATE TABLE `newstop` (
  `id` mediumint(9) NOT NULL,
  `name` text,
  `opisanie` text,
  `razdel` varchar(50) NOT NULL,
  `avtor` text,
  `count` int(10) NOT NULL,
  `comment` int(11) NOT NULL,
  `time` datetime NOT NULL,
  `arhiv` datetime NOT NULL,
  `href` varchar(50) DEFAULT NULL,
  `si` varchar(255) NOT NULL,
  `bi` varchar(255) NOT NULL,
  `metka` varchar(255) NOT NULL,
  `archived` varchar(255) NOT NULL,
  `created_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
  `updated_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
  `thumbnail` varchar(200) NOT NULL,
  `pdf` varchar(200) DEFAULT NULL
) ENGINE=MyISAM DEFAULT CHARSET=utf8;

--
-- Дамп данных таблицы `newstop`
--

INSERT INTO `newstop` (`id`, `name`, `opisanie`, `razdel`, `avtor`, `count`, `comment`, `time`, `arhiv`, `href`, `si`, `bi`, `metka`, `archived`, `created_at`, `updated_at`, `thumbnail`, `pdf`) VALUES
(1, 'Paginator bundle for Symfony!', 'Paginator bundle for Symfony to automate pagination and simplify sorting and other features', 'articlephp', 'Ермаков А.П.', 3272, 11, '2011-11-11 00:00:00', '2015-11-11 00:00:00', '1.php', '1.jpeg', '20090912202040.jpg', 'Symfony', '0', '2020-05-28 22:29:29', '2020-05-28 22:29:29', '', ''),
(2, 'Создание xls(excel) файлов с помощью Spreadsheet_Excel_Writer.', 'Продолжаем изучать возможности php при работе с excel файлами. На этот раз мы рассмотрим создание excel файлов с помощью пакета pear Spreadsheet_Excel_Writer.', 'articlephp', 'Ермаков А.П.', 4353, 5, '2012-01-27 00:00:00', '2015-11-11 00:00:00', '2.php', '2.jpeg', '20090916230741.jpg', '', '0', '2020-05-28 22:29:29', '2020-05-28 22:29:29', '', ''),
(3, 'Погодный информер с jquery, php и mysql', 'В статье я расскажу о создании погодного информера с использованием jquery, php и mysql. В итоге Вы будете иметь погодный информер не хуже чем у Yandex или Rambler.', 'jquery', 'Ермаков А.П.', 47626, 13, '2011-11-11 00:00:00', '2015-11-11 00:00:00', '5.php', '3.jpeg', '20091007193451.png', 'Основы Аякс', '1', '2020-05-28 22:29:29', '2020-05-28 22:29:29', '', ''),
(4, 'Как написать первый плагин на jQuery, с использованием ajax?', 'В статье рассматриваются в основном две функции jquery.fn.extend() и jquery.extend, с помощью которых создаются плагины jQuery. После прочтения Вы научитесь работать с плагинами jQuery, создавать собственные. Статья объясняет функции на основе примера использующего ajax и анимацию с различными эффектами.       \r\n', 'jquery', 'Ермаков А.П.', 4856, 14, '2011-12-04 01:02:00', '2015-11-11 00:00:00', '4.php', '4.jpg', '', 'Основы Аякс', '1', '2020-05-28 22:29:29', '2020-05-28 22:29:29', '', ''),
(5, 'jQuery примеры', '<div>В статье рассматриваются примеры jQuery при взаимодействии с элементами форм, jquery ajax, post, малоизученные функции - jQuery.noConflict(), serialize(). К тому же, после прочтения, Вы научитесь писать ошибкоустойчивый код jquery с другими библиотеками, например с mootools, в заключении я расскажу о постраничном выводе с jquery, php и mysql</div>', 'jquery', 'Ермаков А.П.', 111341, 24, '2015-11-05 19:00:00', '2015-11-11 00:00:00', '3.php', '5.jpg', '', 'Основы Аякс', '1', '2020-05-28 22:29:00', '2020-05-29 09:16:11', '03e4c127a178c1b44fd71daf62f1ce75.jpg', 'диплом.pdf'),
(6, 'Lightbox без фреймовика!', 'Подробно о создании lightbox без использования любых фреймовиков, только на javascript. ', 'javascript', 'Ермаков А.П.', 826, 2, '2011-12-04 00:00:00', '2015-11-11 00:00:00', '7.php', '6.jpg', '', '', '0', '2020-05-28 22:29:29', '2020-05-28 22:29:29', '', ''),
(7, '7 обязательных дополнений для работы в css от Firefox', 'В статье я раcскажу о 5 популярнейших плагинах для FireFox ', 'htmlcss', 'Ермаков А.П.', 513, 2, '2011-12-03 00:00:00', '2015-11-11 00:00:00', '8.php', '7.jpg', '', 'cssmanual', '0', '2020-05-28 22:29:29', '2020-05-28 22:29:29', '', ''),
(8, 'Доздравствует новый Firefox 4!', 'Почитайте о возможностях Firefox 4 с поддержкой CSS трансформаций. ', 'htmlcss', 'Ермаков А.П.', 302, 0, '2011-12-01 00:00:00', '2015-11-11 00:00:00', '9.php', '8.jpg', '', '', '0', '2020-05-28 22:29:29', '2020-05-28 22:29:29', '', ''),
(9, 'Реклама Google – советы, ошибки и  секреты', 'На многих сайтах публикуется информация относительно Google Adsense, многое является правильным, но всё разбросано по разным сайтам, в этой статье автор соберёт полезную информацию о Google Adsense, на основе обобщения в единый материал для изучения.     ', 'seo', 'Ермаков А.П.', 367, 2, '2011-12-02 00:00:00', '2015-11-11 00:00:00', '11.php', '9.jpg', '20100801185912.jpeg', '', '0', '2020-05-28 22:29:29', '2020-05-28 22:29:29', '', ''),
(10, 'Slybox на jQuery', '<div>Узнайте как создать свой lightbox прочитав статью об этом.&nbsp;</div>', 'jquery', 'Ермаков А.П.', 2147, 12, '2020-05-11 23:48:00', '2015-11-11 00:00:00', '6.php', '10.jpg', '20100807230720.jpg', '', '1', '2020-05-28 22:29:00', '2020-05-28 22:29:29', '', ''),
(11, 'jQuery для начинающих!', 'В статье я расскажу о jQuery ajax - jquer.post(),jquery.load(), jquery.ajax. Статья рассчитана не только на начинающих пользователей jQuery, но и на более опытных.', 'jquery', 'Ермаков А.П.', 4298, 8, '2011-12-04 00:00:00', '2015-11-02 00:00:00', '26.php', 'study.jpg', '', 'Основы Аякс', '0', '2020-05-28 22:29:29', '2020-05-28 22:29:29', '', ''),
(12, 'jQuery для начинающего! Продолжение.', 'В статье я расскажу о jQuery методах измерения всего, что измеряется в сравнении с javascript методами. Всё о методах jQuery и javscript (height, width, clientWidth, clientHeight, scrollTop, scrollLeft, scrollHeight, scrollWidth, position, offset и т.д.).', 'jquery', 'Ермаков А.П.', 2701, 4, '2011-12-01 00:00:00', '2015-11-11 00:00:00', '27.php', 'study.jpg', '', 'Основы Аякс', '0', '2020-05-28 22:29:29', '2020-05-28 22:29:29', '', ''),
(13, 'jQuery слайдер', 'jQuery слайдер с методами animate, each, mouseover, mouseout, mousemove, mouseenter, mouseleave, mouseout, lightbox.\r\n', 'jquery', 'Ермаков А.П.', 401, 0, '2015-01-03 00:00:00', '2015-11-12 00:00:00', '14.php', 'nopicjq.jpg', '', 'Основы ', '0', '2020-05-28 22:29:29', '2020-05-28 22:29:29', '', ''),
(14, 'JWPalyer и свой YouTube', 'JWPalyer - самый популярный плагин для работы с видео в интернте - YouTube!', 'jquery', 'Ермаков А.П.', 271, 1, '2011-12-04 00:00:00', '2015-11-11 00:00:00', '15.php', 'youtube.png', '', '', '0', '2020-05-28 22:29:29', '2020-05-28 22:29:29', '', ''),
(15, 'Покупка ссылок у посторонних сайтов', 'Стоит ли покупать ссылки у сайтов или продвигать сайт уникальным содержимым?', 'seo', 'Ермаков А.П.', 54, 0, '2015-01-28 00:00:00', '2015-11-11 00:00:00', '16.php', 'nopics.jpg', 'nopics.jpg', 'php', '0', '2020-05-28 22:29:29', '2020-05-28 22:29:29', '', '');

Вы можете создать ее вручную, можно сделать лучше через команду:

php bin/console doctrine:schema:update --force

Единственное у Вас не получиться обновить таблицу если Вы запустите данную комманду не имея файла myapp/bundles/NewsTopBundle/src/Entity/NewsTop.php аналога в директории myapp/ src/Entity/NewsTop.php
Не знаю почему, но запустить данную команду только внутри бандла у меня не получилось. 

Для того, чтобы указанная команда обновляла сущности изнутри нашего бандла необходимо добавить в файл myapp/config/packages/doctrine.yaml код:

            NewsTop:
                is_bundle: false
                type: annotation
                dir: '%kernel.project_dir%/bundles/NewsTopBundle/src/Entity'
                prefix: 'NewsTop\Entity'
                alias: NewsTop


После создания таблицы в базе данных необходим основанный метод для роутера newstop:

/**
 * @Route("/", name="index", methods={"GET"})
 */
public function index(EntityManagerInterface $em, PaginatorInterface $paginator, Request $request): Response
{
    $events = $this->eventRepository->findAll();

    $dql   = "SELECT n FROM NewsTopBundle:NewsTop n";
    $query = $em->createQuery($dql);
    foreach( $events as &$event) {
        if($event->getSi()) {
            $si = $event->getSi();
            $si = '/img/'.$si;

            if(!is_file($_SERVER['DOCUMENT_ROOT'].$si)) {
                $event->setSi('/img/defaultimg.png');
            } else {
                $event->setSi($si);
            }
        }
    }

    $pagination = $paginator->paginate(
        $query, /* query NOT result */
        $request->query->getInt('page', 1), /*page number*/
        10 /*limit per page*/
    );

    return $this->render('@NewsTop/editor/index.html.twig', [
        'events' => $events,
        'isSoftDeleteEnabled' => $this->isSoftDeleteEnabled,
        'pagination' => $pagination,
    ]);
}

Разберём его подробно.
строка 

$events = $this->eventRepository->findAll();

Предоставляет доступ ко всему набору сущностей Newstop, которые с помощью foreach обходим для обработки изображений, которые являются тестовыми.
Далее следует строка 

pagination = $paginator->paginate(

Ничего сложного это постраничная навигация.

И последним мы отображаем наш шаблон:

return $this->render('@NewsTop/editor/index.html.twig', [
    'events' => $events,
    'isSoftDeleteEnabled' => $this->isSoftDeleteEnabled,
    'pagination' => $pagination,
]);

Код шаблона 

{% extends '@NewsTop/base.html.twig' %}

{% block newstop_title %}Редактор{% endblock %}

{% block newstop_body %}

    <div class="container">
        <div class="row align-items-center mb-3">
            <div class="col">
                <h1>Список мероприятий</h1>
            </div>
            <div class="col text-right">
                <a href="{{ path('editor.new') }}" class="btn btn-light">Добавить</a>
            </div>
        </div>
        <div class="count">
           Всего: {{ pagination.getTotalItemCount }}
        </div>
        <table class="table">
            <thead>
            <tr>
                <th>#</th>
                <th>Название</th>
                <th>Начало</th>
                <th>Конец</th>
                <th>Статус</th>
                <th>Изображение</th>
                <th>Действия</th>
            </tr>
            </thead>
            <tbody>

            {% for event in pagination %}
                <tr>
                    <td>{{ event.id }}</td>
                    <td><a href="{{ path('editor.edit', {'id': event.id}) }}">{{ event.name }}</a></td>
                    <td>
                        {% if event.time is not empty %}
                            {{ event.time | ru_datetime }} — {{ event.arhiv | ru_datetime }}
                        {% else %}
                            {{ event.time | ru_datetime }}
                        {% endif %}
                    </td>
                    <td>
                        {% if event.arhiv is not empty %}
                            {{ event.arhiv | ru_datetime }} — {{ event.arhiv | ru_datetime }}
                        {% else %}
                            {{ event.arhiv | ru_datetime }}
                        {% endif %}
                    </td>

                    <td>{{ event.archived ? 'В архиве' : 'Опубликовано' }}</td>
                    <td>
                        <a class="group1" href="{{ asset( event.si)}}" title="Me and my grandfather on the Ohoopee.">
                            <img src="{{ asset( event.si) | imagine_filter('my_thumb') }}" />
                        </a>
                    </td>
                    <td class="d-flex">
                        <div class="btn-group" role="group">
                            <a href="{{ path('editor.edit', {'id': event.id}) }}" class="btn btn-sm btn-light">
                                <i class="fas fa-pencil-alt"></i>
                            </a>
                            <a href="{{ path('event.show', {'id': event.id}) }}" class="btn btn-sm btn-light" target="_blank">
                                <i class="fas fa-eye"></i>
                            </a>
                            {% if not isSoftDeleteEnabled %}
                                <a href="{{ path('editor.delete', {'id': event.id}) }}" class="btn btn-sm btn-danger"
                                    title="Удалить"><i class="fas fa-trash-alt"></i></a>
                            {% elseif not (isSoftDeleteEnabled and event.archived) %}
                                <a href="{{ path('editor.delete', {'id': event.id}) }}" class="btn btn-sm btn-warning"
                                   title="В архив"><i class="fas fa-trash-alt"></i></a>
                            {% endif %}
                        </div>
                    </td>
                </tr>
            {% else %}
                <tr>
                    <td colspan="9">События не найдены</td>
                </tr>
            {% endfor %}
            </tbody>
        </table>
        <div class="navigation">
            {{ knp_pagination_render(pagination) }}
        </div>
    </div>
{% endblock %}

Сам шаблон twig не сложный. Для стилизации в моём случае подключается также bootstrap, следующим образом:

@import "~@fortawesome/fontawesome-free/css/all.css";
@import "~bootstrap/scss/bootstrap.scss";
@import '~bootstrap/scss/_functions';
@import '~bootstrap/scss/_variables';
@import '~bootstrap/scss/mixins/_breakpoints';

В файле myapp/bundles/NewsTopBundle/assets/scss/widget/_newstop.scss. Если у вас не установлен bootrstrap, установите его следующим образом:

yarn add bootstrap --dev

Также нам понадобится подключить конфигурационные файлы бандла в файле:
myapp/bundles/NewsTopBundle/src/DependencyInjection/NewsTopExtension.php
 

<?php
namespace eap1985\NewsTopBundle\DependencyInjection;

use eap1985\NewsTopBundle\Controller\NewsTopEditorController;
use Exception;
use Liip\ImagineBundle\Service\FilterService;
use Symfony\Component\Config\FileLocator;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Extension\Extension;
use Symfony\Component\DependencyInjection\Loader\YamlFileLoader;

class NewsTopExtension extends Extension
{
    /**
     * Loads a specific configuration.
     *
     * @param array $configs
     * @param ContainerBuilder $container
     * @throws Exception
     */
    public function load(array $configs, ContainerBuilder $container)
    {

Метод load должен содержать подключаемые файлы yaml:

$loader = new YamlFileLoader(
    $container,
    new FileLocator(__DIR__.'/../../config')
);
$loader->load('services.yaml');

Дополнительные параметры внутри метода load носят тестовый характер. Так же как и сам бандл. В дальнейшем будем надеется что весь бандл мы сможем выложить на packagist.org
Не забудьте про файл myapp/bundles/NewsTopBundle/config/packeges/liip_imagine.yaml, в нём указываются настройки бандла liip_imagine
После этого Вы должны увидеть примерно следующую структуру.
Установка Symfony 5. Установка своего бандла.

Если, что-то не получилось, скачайте код бандла из репозитория .

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

Взаимосвязанные материалы

# 2. Установка Symfony 5. Часть 1. Установка своего бандла. (пятница, мая 29, 2020 - 19:06 ),

1. Основная установка.

читать...

На разработку сайта! Скидки до 20%!