Ответы пользователя по тегу Symfony
  • JsonResponse убирает индексы массива из результата, если они начинаются с нуля?

    BoShurik
    @BoShurik Куратор тега Symfony
    Symfony developer
    JsonResponse не при чем. Дело в json_encode. Вам нужна опция JSON_FORCE_OBJECT

    use Symfony\Component\HttpFoundation\JsonResponse;
    
    $data = [
        'List' => [
            0 => [
                'id' => 'id0',
            ],
            1 => [
                'id' => 'id1',
            ],
        ],
    ];
    dump(
        new JsonResponse(
            json_encode($data, JsonResponse::DEFAULT_ENCODING_OPTIONS | JSON_FORCE_OBJECT),
            200,
            [],
            true
        )
    );
    // or
    $response = new JsonResponse($data);
    $response->setEncodingOptions(JsonResponse::DEFAULT_ENCODING_OPTIONS | JSON_FORCE_OBJECT);
    dump($response->getContent());


    Либо, если это нужно регулярно и много где, можно создать свой JsonResponse, который наследуется от стандартного, в котором изменить encodingOptions на нужные
    Ответ написан
    Комментировать
  • Как вы получаете количество оставшихся записей для выборки подмножеств?

    BoShurik
    @BoShurik Куратор тега Symfony
    Symfony developer
    https://github.com/BabDev/Pagerfanta

    use Pagerfanta\Pagerfanta;
    
    class PaginatedCollection
    {
        private int $page;
        private int $size;
        private int $total;
        private int $pages;
        private array $items;
    
        public function __construct(Pagerfanta $pagerfanta)
        {
            $this->page = $pagerfanta->getCurrentPage();
            $this->size = $pagerfanta->getMaxPerPage();
            $this->total = $pagerfanta->getNbResults();
            $this->pages = $pagerfanta->getNbPages();
            $this->items = iterator_to_array($pagerfanta);
        }
    
        public function getPage(): int
        {
            return $this->page;
        }
    
        public function getSize(): int
        {
            return $this->size;
        }
    
        public function getTotal(): int
        {
            return $this->total;
        }
    
        public function getPages(): int
        {
            return $this->pages;
        }
    
        public function getItems(): array
        {
            return $this->items;
        }
    }


    // Repository
    public function findPaginatedBySupplierId(int $supplier_id)
    {
        return new Pagerfanta(DoctrineORMAdapter($this->getSupplierIdQueryBuilder($supplier_id)));
    }
    
    private function getSupplierIdQueryBuilder(int $supplier_id)
    {
        $qb = $this->createQueryBuilder('sj');
        $qb = $qb
            ->select("sj")
            ->orderBy('sj.datetime', 'ASC')
            ->andWhere("sj.supplier = :supplier_id")
            ->setParameter("supplier_id", $supplier_id);
    
        return $qb;
    }


    // Controller
    public function action()
    {
        // ...
        return $this->json($this->paginatedCollection($repository->findPaginatedBySupplierId($id), $request));
    }
    
    // AbstractController
    protected function paginatedCollection(Pagerfanta $pagination, Request $request, int $size = 20): PaginatedCollection
    {
        $pagination = $this->paginate($pagination, $request, $size);
    
        return new PaginatedCollection($pagination);
    }
    
    protected function paginate(Pagerfanta $pagination, Request $request, int $size = 20): Pagerfanta
    {
        $pagination->setMaxPerPage($size);
        $pagination->setCurrentPage($request->query->getInt('page', 1));
    
        return $pagination;
    }


    {
        "page": 1,
        "size": 20,
        "total": 76,
        "pages": 4,
        "items": [...]
    }
    Ответ написан
  • Как десериализовать массив json-ов c отношением OneToMany?

    BoShurik
    @BoShurik Куратор тега Symfony
    Symfony developer
    Symfony serializer как десериализовать массив с объектами?

    Плюс, вам надо прописать типы в phpDoc
    /**
     * @var \App\Entity\IpContract[]
     *
     * @ORM\OneToMany(targetEntity="App\Entity\IpContract", mappedBy="users", orphanRemoval=true)
     */
    private $ipContracts;
    Ответ написан
  • Избавиться от __initializer__, __cloner__, __isInitialized__ при выводе json?

    BoShurik
    @BoShurik Куратор тега Symfony
    Symfony developer
    1. Создать свой normalizer
    2. Игнорировать эти атрибуты
    return $this->json($user, 200, [], [
        AbstractNormalizer::IGNORED_ATTRIBUTES => ['__initializer__', '__cloner__', '__isInitialized__'],
    ]);

    3. Пробросить это глобально в ObjectNormalizer
    // App\Kernel
    protected function configureContainer(ContainerBuilder $container, LoaderInterface $loader)
    {
        // ...
        $container->addCompilerPass(new class implements CompilerPassInterface {
            public function process(ContainerBuilder $container)
            {
                $container->getDefinition('serializer.normalizer.object')->setArgument(6, [
                    AbstractNormalizer::IGNORED_ATTRIBUTES => ['__initializer__', '__cloner__', '__isInitialized__'],
                ]);
            }
        });
    }
    Ответ написан
    Комментировать
  • Как установить lazy load сервису League\Flysystem\Adapter\Local?

    BoShurik
    @BoShurik Куратор тега Symfony
    Symfony developer
    // App\Kernel
    protected function configureContainer(ContainerBuilder $container, LoaderInterface $loader): void
    {
        // ...
        $container->addCompilerPass(new class implements CompilerPassInterface {
            public function process(ContainerBuilder $container)
            {
                $container->getDefinition('oneup_flysystem.adapter.local')->setLazy(true);
            }
        });
    }
    Ответ написан
    6 комментариев
  • Ошибка при выводе данных в Symfony twig?

    BoShurik
    @BoShurik Куратор тега Symfony
    Symfony developer
    В вашем случае для $created_at геттером должен быть getCreated_at, но это будет нарушать PSR-1

    В Symfony используется camelCase для свойств объекта. В вашем случае надо переименовать в $createdAt

    Подробнее тут и тут
    Ответ написан
  • Как заигнорить часть framework.yaml?

    BoShurik
    @BoShurik Куратор тега Symfony
    Symfony developer
    По-хорошему, это решается через php.ini, как рекомендовано в документации
    Since every developer uses a different IDE, the recommended way to enable this feature is to configure it on a system level. This can be done by setting the xdebug.file_link_format option in your php.ini configuration file.

    Но если очень хочется, то можно сделать так:
    # services.yaml
    imports:
        - { resource: 'local.yaml', ignore_errors: true }


    # local.yaml
    framework:
        ide: 'phpstorm://open?file=%%f&line=%%l'

    # .gitignore
    /config/local.yaml

    local.yaml создаете сами локально и вносите туда все конфиги, которые не надо комитить

    Применимо для всех версий symfony*
    Ответ написан
    1 комментарий
  • Как отслеживать новые записи в базе онлайн?

    BoShurik
    @BoShurik Куратор тега Symfony
    Symfony developer
    WebSocket: Centrifugo + CentrifugoBundle
    Mercure: Mercure + MercureBundle

    Если нет возможности поставить что-то на сервер, то остается решение от Олег, но лучший код - не написанный код, а в случае с Ratchet будет больше boilerplate'а
    Ответ написан
    Комментировать
  • Symfony - можно ли использовать CKEDITOR5 вместе с FOSCKEditorBundle?

    BoShurik
    @BoShurik Куратор тега Symfony
    Symfony developer
    На данный момент нет. Отслеживать статус можно тут: https://github.com/FriendsOfSymfony/FOSCKEditorBun...
    Ответ написан
    Комментировать
  • Symfony 4 - Как решить проблему с Entity в Bundle?

    BoShurik
    @BoShurik Куратор тега Symfony
    Symfony developer
    Вам нужен Mapped Superclass

    // Этот класс в бандле
    /** @MappedSuperclass */
    class BaseAnimal
    {
        /** @Column(type="string") */
        protected $name;
    
        /** @Column(type="integer") */
        protected $size;
        
        /** @Column(type="boolean")  */
        protected $canFly;
    }
    
    // Этот класс в проекте
    /** @Entity */
    class Animal extends BaseAnimal
    {
        /** @Column(type="string") */
        private $id;
    
        /** @ManyToOne(targetEntity="Person")  */
        private $owner;
    }
    Ответ написан
    Комментировать
  • Component\Validator - Как сделать проверку на "Отсутствует или массив"?

    BoShurik
    @BoShurik Куратор тега Symfony
    Symfony developer
    Вам нужна опция allowMissingFields

    $validator = (new ValidatorBuilder())->getValidator();
    $constraints = [
        new Assert\Collection([
            'fields' => [
                'address' => [
                    new Assert\NotNull(),
                    new Assert\Type('array'),
                ],
            ],
            'allowMissingFields' => true,
            'allowExtraFields' => true,
        ]),
        new Assert\Collection([
            'fields' => [
                'required' => [
                    new Assert\NotNull(),
                ],
            ],
            'allowExtraFields' => true,
        ]),
    ];
    
    $data = [];
    dump($data);
    dump($validator->validate($data, $constraints)); // Ko
    
    $data = [
        'address' => null,
    ];
    dump($data);
    dump($validator->validate($data, $constraints)); // Ko
    
    $data = [
        'address' => [],
    ];
    dump($data);
    dump($validator->validate($data, $constraints)); // Ko
    
    $data = [
        'address' => 'address',
    ];
    dump($data);
    dump($validator->validate($data, $constraints)); // Ko
    
    $data = [
        'address' => [],
        'required' => 'value',
    ];
    dump($data);
    dump($validator->validate($data, $constraints)); // Ok
    Ответ написан
    6 комментариев
  • Как в Symfony 5 проверить роль любого пользователя?

    BoShurik
    @BoShurik Куратор тега Symfony
    Symfony developer
    Security работает именно с залогиненым пользователем. Если вам надо проверить другого пользователя, то надо делать что-то вроде:
    /** @var UserInterface $user */
    /** @var RoleHierarchyInterface $hierarchy */
    
    $roles = $hierarchy->getReachableRoleNames($user->getRoles());
    $result = array_search('ROLE_GROUP_MANAGER', $roles) !== false;
    Ответ написан
    1 комментарий
  • Opensource CMS на symfony?

    BoShurik
    @BoShurik Куратор тега Symfony
    Symfony developer
    Как пример - Sylius (хотя это скорее CMF)

    Но для обучения я бы все-таки рекомендовал официальное демо-приложение и документацию. CMS - это уже несколько другой уровень абстракции, чем фреймворк. Весьма вероятно там будут свои соглашения, которые к фреймворку иметь отношение не будут (Sylius как раз хороший пример этого)
    Ответ написан
    1 комментарий
  • Как загрузить роутинги сразу из нескольких файлов?

    BoShurik
    @BoShurik Куратор тега Symfony
    Symfony developer
    Если вы знаете расположение конфигов, то можно так:
    use Symfony\Component\Config\FileLocator;
    use Symfony\Component\Routing\Loader\YamlFileLoader;
    use Symfony\Component\Routing\RouteCollectionBuilder;
    
    require_once __DIR__.'/../vendor/autoload.php';
    
    $locator = new FileLocator([__DIR__.'/../data']);
    $loader = new YamlFileLoader($locator);
    
    $builder = new RouteCollectionBuilder($loader);
    $builder->import('dir1/routes.yaml');
    $builder->import('dir2/routes.yaml');
    
    $routes = $builder->build();
    var_dump($routes);


    Если расположение конфигов неизвестно или вы просто не хотите заморачиваться при добавлении новых:
    use Symfony\Component\Config\FileLocator;
    use Symfony\Component\Config\Loader\DelegatingLoader;
    use Symfony\Component\Config\Loader\LoaderResolver;
    use Symfony\Component\Routing\Loader\GlobFileLoader;
    use Symfony\Component\Routing\Loader\YamlFileLoader;
    use Symfony\Component\Routing\RouteCollectionBuilder;
    
    require_once __DIR__.'/../vendor/autoload.php';
    
    $locator = new FileLocator([__DIR__.'/../data']);
    $resolver = new LoaderResolver([
        new GlobFileLoader($locator), // needs symfony/finder
        new YamlFileLoader($locator),
    ]);
    $loader = new DelegatingLoader($resolver);
    
    $builder = new RouteCollectionBuilder($loader);
    $builder->import('**/*/routes.yaml', '/', 'glob');
    
    $routes = $builder->build();
    var_dump($routes);
    Ответ написан
    Комментировать
  • Symfony, кто знает как замэппить значение из Request?

    BoShurik
    @BoShurik Куратор тега Symfony
    Symfony developer
    В общем случае - никак.
    Workaround: через value object:
    class Locale
    {
        /**
         * @var string
         */
        private $value;
    
        public function __construct(string $value)
        {
            $this->value = $value;
        }
    
        public function __toString()
        {
            return $this->value;
        }
    }
    
    class LocaleFactory
    {
        /**
         * @var RequestStack
         */
        private $requestStack;
    
        /**
         * @var string
         */
        private $default;
    
        public function __construct(RequestStack $requestStack, string $default)
        {
            $this->requestStack = $requestStack;
            $this->default = $default;
        }
    
        public function create(): Locale
        {
            if ($request = $this->requestStack->getMasterRequest()) {
                return new Locale($request->get('locale', $this->default));
            }
    
            return new Locale($this->default);
        }
    }

    services:
        App\Locale\Locale:
            factory: ['@App\Locale\LocaleFactory', 'create']


    Соответственно использовать не как скалярное значение
    public function add(Request $request, EntityManagerInterface $manager, Locale $lang)


    Action Argument Resolver / ParamConverter - тоже вариант, но будет работать только для контроллеров.
    Ответ написан
    Комментировать
  • Как передать объект с параметром в Service Container?

    BoShurik
    @BoShurik Куратор тега Symfony
    Symfony developer
    Можно так: https://symfony.com/doc/current/service_container/...
    А можно так: https://symfony.com/doc/current/service_container/...
    Или даже так: https://symfony.com/doc/current/service_container/...

    В первом случае зарегистрировать ваш DateInterval как сервис, во втором - создавать его внутри конфигуратора/фабрики.
    Ответ написан
  • А как работать с Symfony Routing отдельным компонентом?

    BoShurik
    @BoShurik Куратор тега Symfony
    Symfony developer
    Это уже зависит от вашего приложения.
    Можно, к примеру, использовать https://github.com/PHP-DI/Invoker, будет что-то вроде

    $parameters = $matcher->match($request->getPathInfo());
    $invoker = new Invoker\Invoker(null, $container);
    $invoker->call($parameters['_controller'], $parameters);


    Если без контейнера, то что-то вроде
    $parameters = $matcher->match($request->getPathInfo());
    list($class, $action) = $parameters['_controller'];
    unset($parameters['_controller']);
    $controller = new $class;
    $controller->$action($parameters);

    Но передача аргументов через массив - не очень удобно.
    Ответ написан
    3 комментария
  • Почему я не могу получить entity с репозитория в Doctrine?

    BoShurik
    @BoShurik Куратор тега Symfony
    Symfony developer
    https://www.doctrine-project.org/projects/doctrine...

    referencedColumnName: Name of the primary key identifier that is used for joining of this relation.


    /**
     * @OneToMany(targetEntity="Address", mappedBy="customer", cascade={"persist", "remove"})
     * @JoinColumn(name="customer_id", referencedColumnName="customer_id")
     */
    protected $addresses;


    Address::$customer_id - это primary key?

    Не заметил, что вы JoinColumn применяете к OneToMany. Оно не применимо, должно быть что-то вроде этого:
    /**
     * @Entity
     * @Table(name="`address`")
     */
    class Address
    {
        /**
         * @Id
         * @Column(type="integer")
         * @GeneratedValue(strategy="IDENTITY")
         */
        protected $address_id;
        /**
         * @Column(type="string")
         */
        protected $firstname;
        /**
         * @ManyToOne(targetEntity="Customer", inversedBy="address")
         * @JoinColumn(name="customer_id", referencedColumnName="customer_id")
         */
        protected $customer;
    }
    
    /**
     * @Entity(repositoryClass="\Core\Repositories\CustomerRepository")
     * @Table(name="`customer`")
     */
    class Customer
    {
        /**
         * @Id
         * @Column(type="integer")
         * @GeneratedValue(strategy="IDENTITY")
         */
        protected $customer_id;
        
        /**
         * @OneToMany(targetEntity="Address", mappedBy="customer", cascade={"persist", "remove"})
         */
        protected $addresses;
    
        public function __construct()
        {
            $this->addresses = new ArrayCollection();
        }
    }
    Ответ написан
    8 комментариев
  • Почему join column в Doctrine присваивается null перед flush?

    BoShurik
    @BoShurik Куратор тега Symfony
    Symfony developer
    https://www.doctrine-project.org/projects/doctrine...

    referencedColumnName: Name of the primary key identifier that is used for joining of this relation.


    /**
     * @OneToOne(targetEntity="\Core\Entities\Delivery\BoxberryTtn", mappedBy="ttn_boxberry", cascade={"persist", "remove"})
     * @JoinColumn(name="order_code", referencedColumnName="id")
     */
    protected $boxberry_ttn;


    Если хочется, чтоб была связь через order_code, то сделайте его primary key:
    /**
     * @Entity
     * @Table(name="`ttn_boxberry`")
     */
    class BoxberryTtn
    {
        /**
         * @Id
         * @Column(type="string")
         * @GeneratedValue(strategy="NONE")
         */
        protected $id;
        
        public function __construct(string $id)
        {
            $this->id = $tid;
        }
    }


    Плюс, у вас два поля order_code
    /**
     * @Entity
     * @Table(name="`order`")
     */
    class Order
    {
        /**
         * @Id
         * @Column(type="integer")
         */
        protected $order_code;
    
        /**
         * @OneToOne(targetEntity="\Core\Entities\Delivery\BoxberryTtn", mappedBy="ttn_boxberry", cascade={"persist", "remove"})
         * @JoinColumn(name="order_code", referencedColumnName="order_code")
         */
        protected $boxberry_ttn;
    }


    Они так же друг другу могут перезаписывать

    В идеале должно быть что-то вроде этого

    /**
     * @Entity
     * @Table(name="`order`")
     */
    class Order
    {
        /**
         * @Id
         * @Column(type="integer")
         */
        protected $order_id;
    
        /**
         * @Id
         * @Column(type="integer")
         */
        protected $order_code;
    
        /**
         * @Column(type="integer")
         */
        protected $warehouse_id;
    
        /**
         * @OneToOne(targetEntity="\Core\Entities\Delivery\BoxberryTtn", cascade={"persist", "remove"})
         * @JoinColumn(referencedColumnName="order_code")
         */
        protected $boxberry_ttn;
    
        public function __construct(int $order_id, string $order_code)
        {
            $this->order_id = $order_id;
            $this->order_code = $order_code;
        }
    }
    
    /**
     * @Entity
     * @Table(name="`ttn_boxberry`")
     */
    class BoxberryTtn
    {
        /**
         * @Id
         * @Column(type="string")
         * @GeneratedValue(strategy="NONE")
         */
        protected $order_code;
    
        /**
         * @Column(type="string")
         */
        protected $ttn_num;
    
        /**
         * @Column(type="float", scale=2)
         */
        protected $delivery_cost;
    
        public function __construct(Order $order)
        {
            $this->order_code = $order->getOrderCode();
        }
    }

    Ответ написан
    3 комментария
  • Как исправить ошибку в Symfony 5.0 An exception has been thrown during the rendering of a template?

    BoShurik
    @BoShurik Куратор тега Symfony
    Symfony developer
    Проблема в maker-bundle (не привязан к версии Symfony). Он не создает магический метод __toString, чтобы отображать сущности. Вам надо добавить его самому:
    public function __toString()
    {
        return $this->name;
    }


    Но в вашем случае проблема в том, что сущность называется Color и для нее создается форма с названием  ColorType. Для ее рендера используется шаблон формы
    \Symfony\Component\Form\Extension\Core\Type\ColorType
    в котором, судя по всему, ваша сущность кастится в строку.

    Добавьте в классе \App\Form\ColorType метод getBlockPrefix и все заработает:
    public function getBlockPrefix()
    {
        return 'app_color';
    }
    Ответ написан
    2 комментария