• Symfony - можно ли использовать CKEDITOR5 вместе с FOSCKEditorBundle?

    BoShurik
    @BoShurik Куратор тега Symfony
    Symfony developer
    На данный момент нет. Отслеживать статус можно тут: https://github.com/FriendsOfSymfony/FOSCKEditorBun...
    Ответ написан
    Комментировать
  • Почему значения всех свойств класса "затираются" в конструкторе?

    BoShurik
    @BoShurik
    Symfony developer
    У вас ошибка
    class Decoder{ 
        private $inputStr; 
        private $pos; 
        private $out; 
        private $length; 
         
        public function __construct(string $str) {
            $this->inputStr = $str;        
            $this->pos = 0; // Было $this->$pos
            $this->out = ''; // Было $this->$out
            $this->length = mb_strlen($str); // Было $this->$length
        }   
        public function decode(): string {
            return $this->out; // // Было $this->$out
        }    
    }


    В вашем случае $this->$length = mb_strlen($str); => $this->null = mb_strlen($str);
    Таким образом return $this->$out; => return $this->null // 6
    Ответ написан
    Комментировать
  • 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 - тоже вариант, но будет работать только для контроллеров.
    Ответ написан
    Комментировать
  • Как перенести проект на Symfony с одного сайта на другой?

    BoShurik
    @BoShurik
    Symfony developer
    Скорее всего на предыдущем хостинге использовался nginx, а на новом хостинге apache. Надо добавить .htaccess с соответствующими правилами
    Ответ написан
    Комментировать
  • Как у вас организована одновременная разработка библиотеки и использующего ее приложения?

    BoShurik
    @BoShurik
    Symfony developer
    Вы можете подтягивать зависимости из директории:
    https://getcomposer.org/doc/05-repositories.md#path
    Единственное, после разработки этот параметр придется убрать, чтобы на проде зависимость подтягивалась из гита

    Как вариант использовать preffered-install
    "config": {
        "preferred-install": {
            "my/lib": "source"
        }
    },

    Т.о. в директории venodr/my/lib будет содержаться git репозиторий и можно будет делать коммиты прямо от туда.

    Лично я остановился на костыльном варианте :) Просто делаю папку venodr/my/lib - симлинком на директорию, в которой лежит проект с этой либой. Что-то вроде первого вариант, но не надо будет подчищать за собой composer.json. В итоге получается так:
    • веду разработку библиотеки в отдельной директории/проекте
    • делаю симлинк директории в вендор и проверяю работу в приложениях
    • если все ок, тегаю релиз
    • удаляю симлинк (не обязательно, но тогда в проекте библиотеки git будет в статусе detached head, что не критично)
    • делаю composer update
    Ответ написан
  • Как передать объект с параметром в 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 комментария
  • Как образом можно вывести такую структуру?

    BoShurik
    @BoShurik
    Symfony developer
    <?php
    $items = range(1, 30);
    $chunks = array_chunk($items, 5);
    $chunks = array_map(function($items){
        return array_chunk($items, 4);
    }, $chunks);
    ?>
    
    <?php foreach ($chunks as $chunk): ?>
        <div class="item">
            <div class="item-left">
                <?php foreach ($chunk[0] as $item): ?>
                    <div class="point"><?=$item?></div>
                <?php endforeach; ?>
            </div>
            <div class="item-right">
                <?php foreach ($chunk[1] as $item): ?>
                    <div class="point"><?=$item?></div>
                <?php endforeach; ?>
            </div>
        </div>
    <?php endforeach; ?>


    Если элементов не всегда 30, то надо делать проверку на существование $chunk[1]
    Ответ написан
    Комментировать
  • Проблема в каждом втором запросе?

    BoShurik
    @BoShurik Куратор тега Symfony
    Symfony developer
    Вам нужно либо реализовать примитивный провайдер, который будет возвращать new User() вместо вашего кода
    public function getUser($credentials, UserProviderInterface $userProvider)
    {
        return new User();
    }

    либо добавить stateless: true
    Проблема в ContextListener который
    ContextListener manages the SecurityContext persistence through a session.

    Он ожидает, что в сессии находится токен пользователя и пытается его восстановить из провайдера, которого у вас нет.
    Ответ написан
    2 комментария
  • Как работает ЭЦП на сайте?

    BoShurik
    @BoShurik
    Symfony developer
    По поводу аутентификации вот статья на хабре: https://habr.com/ru/post/123372/, только в вашем случае не надо самому генерировать ключи

    Соответственно вам придется поставить КриптоПро CSP для проверки подписи на сервере. Для подписи сообщений в чате и документов хватит плагина и КриптоПро на клиенте.

    Примеры работы с плагином есть на сайте: https://cpdn.cryptopro.ru/default.asp?url=content/... на их форуме можно задавать вопросы, они оперативно отвечают
    Ответ написан
    Комментировать
  • Symfony serializer как десериализовать массив с объектами?

    BoShurik
    @BoShurik Куратор тега Symfony
    Symfony developer
    $reflectionExtractor = new ReflectionExtractor();
    $phpDocExtractor = new PhpDocExtractor();
    $propertyTypeExtractor = new PropertyInfoExtractor([$reflectionExtractor], [$phpDocExtractor, $reflectionExtractor], [$phpDocExtractor], [$reflectionExtractor], [$reflectionExtractor]);
    
    $normalizer = new ObjectNormalizer(null, null, null, $propertyTypeExtractor);
    $arrayNormalizer = new ArrayDenormalizer();
    $serializer = new Serializer([$arrayNormalizer, $normalizer]);
    return $serializer->denormalize($array, ContragentDto::class);
    Ответ написан
    24 комментария