Задать вопрос
  • ООП. Как избежать передачи флаг как аргумента функции?

    @sgidlev Автор вопроса
    UPD: 06.06.23
    Реализовал через фабрику

    EmployeeCreateInsertion
    <?php
    declare(strict_types=1);
    
    namespace App\Feature\Management\Factory;
    
    use App\Feature\Management\Entity\ManagementEmployee;
    use App\Feature\Management\Exception\ManagementEmployeeAlreadyExistsException;
    use App\Feature\Management\Model\Employee\ManagementEmployeeRequest;
    use App\Feature\Management\Model\Employee\Upsert\ManagementEmployeeUpsert;
    use App\Feature\Management\Repository\ManagementEmployeeRepository;
    use App\Feature\Management\Repository\ManagementRepository;
    use App\Feature\User\Repository\UserRepository;
    
    class EmployeeCreateInsertion extends EmployeeInsertion
    {
        public function __construct(
            private readonly ManagementEmployeeRepository $employeeRepository,
            private readonly ManagementEmployeeUpsert     $upsert,
            private readonly ManagementRepository         $managementRepository,
            private readonly UserRepository               $userRepository,
        )
        {
        }
    
        public function insert(ManagementEmployee $employee, ManagementEmployeeRequest $request): ManagementEmployee
        {
            $management = $this->managementRepository->getManagement($request->getManagementId());
            $user = $this->userRepository->getUser($request->getUserId());
    
            $employee->setManagement($management);
            $employee->setUser($user);
    
            $this->checkEmployeeIsExist($employee);
            $this->upsert->isUserHired($employee, $request);
    
            return $this->upsert->fill($employee, $request);
        }
    
        public function checkEmployeeIsExist(ManagementEmployee $employee): void
        {
            if ($this->employeeRepository->findByManagementAndUser($employee->getManagement(), $employee->getUser())) {
                throw new ManagementEmployeeAlreadyExistsException();
            }
        }
    }


    EmployeeInsertion
    <?php
    declare(strict_types=1);
    
    namespace App\Feature\Management\Factory;
    
    use App\Feature\Management\Entity\ManagementEmployee;
    use App\Feature\Management\Model\Employee\ManagementEmployeeRequest;
    
    abstract class EmployeeInsertion
    {
        abstract public function insert(ManagementEmployee $employee, ManagementEmployeeRequest $request): ManagementEmployee;
    }


    EmployeeInsertionFactory
    <?php
    declare(strict_types=1);
    
    namespace App\Feature\Management\Factory;
    
    use App\Application\Enum\InsertType;
    
    interface EmployeeInsertionFactory
    {
        public function makeInsertion(InsertType $insertType): EmployeeInsertion;
    }


    EmployeeInsertionFactoryImpl
    <?php
    declare(strict_types=1);
    
    namespace App\Feature\Management\Factory;
    
    use App\Application\Enum\InsertType;
    use App\Infrastructure\Exception\InvalidInsertTypeException;
    
    readonly class EmployeeInsertionFactoryImpl implements EmployeeInsertionFactory
    {
        public function __construct(
            private EmployeeCreateInsertion $createInsertion,
            private EmployeeUpdateInsertion $updateInsertion
        )
        {
    
        }
    
        public function makeInsertion(InsertType $insertType): EmployeeInsertion
        {
            return match ($insertType) {
                InsertType::CREATE => $this->createInsertion,
                InsertType::UPDATE => $this->updateInsertion,
                default => throw new InvalidInsertTypeException(),
            };
        }
    }


    EmployeeUpdateInsertion
    <?php
    declare(strict_types=1);
    
    namespace App\Feature\Management\Factory;
    
    use App\Feature\Management\Entity\ManagementEmployee;
    use App\Feature\Management\Exception\ManagementEmployeeAlreadyExistsException;
    use App\Feature\Management\Model\Employee\ManagementEmployeeRequest;
    use App\Feature\Management\Model\Employee\Upsert\ManagementEmployeeUpsert;
    use App\Feature\Management\Repository\ManagementEmployeeRepository;
    use App\Feature\Management\Repository\ManagementRepository;
    use App\Feature\User\Repository\UserRepository;
    
    class EmployeeUpdateInsertion extends EmployeeInsertion
    {
        public function __construct(
            private readonly ManagementEmployeeRepository $employeeRepository,
            private readonly ManagementEmployeeUpsert     $upsert,
            private readonly ManagementRepository         $managementRepository,
            private readonly UserRepository               $userRepository,
        )
        {
        }
    
        public function insert(ManagementEmployee $employee, ManagementEmployeeRequest $request): ManagementEmployee
        {
            $management = $this->managementRepository->getManagement($request->getManagementId());
            $user = $this->userRepository->getUser($request->getUserId());
    
            $employee->setManagement($management);
    
            if ($this->upsert->isUserNotChanged($employee, $request)) {
                return $this->upsert->fill($employee, $request);
            }
    
            $employee->setUser($user);
    
            $this->isEmployeeExist($employee, $request);
            $this->upsert->isUserHired($employee, $request);
    
            return $this->upsert->fill($employee, $request);
        }
    
        public function isEmployeeExist(ManagementEmployee $employee, ManagementEmployeeRequest $request): void
        {
            if ($this->employeeRepository->findByManagementAndUser($employee->getManagement(), $employee->getUser())
            ) {
                throw new ManagementEmployeeAlreadyExistsException();
            }
        }
    }


    ManagementEmployeeService
    <?php
    declare(strict_types=1);
    
    namespace App\Feature\Management\Service;
    
    use App\Application\Enum\InsertType;
    use App\Application\Model\Response\IdResponse;
    use App\Feature\Management\Entity\ManagementEmployee;
    use App\Feature\Management\Factory\EmployeeInsertionFactoryImpl;
    use App\Feature\Management\Mapper\ManagementEmployeeMapper;
    use App\Feature\Management\Mapper\ManagementMapper;
    use App\Feature\Management\Model\Employee\ManagementEmployeeDetails;
    use App\Feature\Management\Model\Employee\ManagementEmployeeRequest;
    use App\Feature\Management\Repository\ManagementEmployeeRepository;
    use App\Feature\User\Mapper\UserMapper;
    
    readonly class ManagementEmployeeService
    {
        public function __construct(
            private ManagementEmployeeRepository $employeeRepository,
            private EmployeeInsertionFactoryImpl $factory
        )
        {
        }
    
        public function getEmployee(int $id): ManagementEmployeeDetails
        {
            $employee = $this->employeeRepository->getById($id);
    
            return ManagementEmployeeMapper::mapEmployeeDetails($employee)
                ->setUser(UserMapper::mapUser($employee->getUser()))
                ->setManagement(ManagementMapper::mapManagement($employee->getManagement()));
        }
    
        public function deleteEmployee(int $id): void
        {
            $employee = $this->employeeRepository->getById($id);
            $this->employeeRepository->removeAndCommit($employee);
        }
    
        public function createEmployee(ManagementEmployeeRequest $request): IdResponse
        {
            $employee = $this->factory
                ->makeInsertion(InsertType::CREATE)
                ->insert(new ManagementEmployee(), $request);
    
            $this->employeeRepository->saveAndCommit($employee);
    
            return new IdResponse($employee->getId());
        }
    
        public function updateEmployee(int $id, ManagementEmployeeRequest $request): void
        {
            $employee = $this->factory
                ->makeInsertion(InsertType::UPDATE)
                ->insert($this->employeeRepository->getById($id), $request);
    
            $this->employeeRepository->saveAndCommit($employee);
        }
    
    }


    InsertType
    <?php
    declare(strict_types=1);
    
    namespace App\Application\Enum;
    
    enum InsertType: int implements BaseEnumInterface
    {
        use BaseEnumTrait;
    
        case CREATE = 1; // on create request
        case UPDATE = 2; // on update request
    }
  • ООП. Как избежать передачи флаг как аргумента функции?

    @sgidlev Автор вопроса
    Класс по обработке данных при создании сотрудника
    CreateManagementEmployeeUpsert
    <?php
    declare(strict_types=1);
    
    namespace App\Feature\Management\Model\Employee\Upsert;
    
    use App\Feature\Management\Entity\ManagementEmployee;
    use App\Feature\Management\Exception\ManagementEmployeeAlreadyExistsException;
    use App\Feature\Management\Model\Employee\ManagementEmployeeRequest;
    use App\Feature\Management\Repository\ManagementEmployeeRepository;
    use App\Feature\Management\Repository\ManagementRepository;
    use App\Feature\User\Repository\UserRepository;
    
    class CreateManagementEmployeeUpsert extends AbstractManagementEmployeeUpsert
    {
        public function __construct(
            private readonly ManagementEmployeeRepository $employeeRepository,
            private readonly ManagementEmployeeUpsert     $upsert,
            private readonly ManagementRepository         $managementRepository,
            private readonly UserRepository               $userRepository,
        )
        {
        }
    
        public function fill(ManagementEmployee $employee, ManagementEmployeeRequest $request): ManagementEmployee
        {
            $management = $this->managementRepository->getManagement($request->getManagementId());
            $user = $this->userRepository->getUser($request->getUserId());
    
            $employee->setManagement($management);
            $employee->setUser($user);
    
            $this->checkEmployeeIsExist($employee);
            $this->upsert->isUserHired($employee, $request);
    
            return $this->upsert->fill($employee, $request);
        }
    
        public function checkEmployeeIsExist(ManagementEmployee $employee): void
        {
            if ($this->employeeRepository->findByManagementAndUser($employee->getManagement(), $employee->getUser())) {
                throw new ManagementEmployeeAlreadyExistsException();
            }
        }
    
    }


    Класс по обработке данных при обновлении сотрудника
    UpdateManagementEmployeeUpsert
    <?php
    declare(strict_types=1);
    
    namespace App\Feature\Management\Model\Employee\Upsert;
    
    use App\Feature\Management\Entity\ManagementEmployee;
    use App\Feature\Management\Exception\ManagementEmployeeAlreadyExistsException;
    use App\Feature\Management\Model\Employee\ManagementEmployeeRequest;
    use App\Feature\Management\Repository\ManagementEmployeeRepository;
    use App\Feature\Management\Repository\ManagementRepository;
    use App\Feature\User\Repository\UserRepository;
    
    class UpdateManagementEmployeeUpsert extends AbstractManagementEmployeeUpsert
    {
        public function __construct(
            private readonly ManagementEmployeeRepository $employeeRepository,
            private readonly ManagementEmployeeUpsert     $upsert,
            private readonly ManagementRepository         $managementRepository,
            private readonly UserRepository               $userRepository,
        )
        {
        }
    
        public function fill(ManagementEmployee $employee, ManagementEmployeeRequest $request): ManagementEmployee
        {
            $management = $this->managementRepository->getManagement($request->getManagementId());
            $user = $this->userRepository->getUser($request->getUserId());
    
            $employee->setManagement($management);
    
            if ($this->upsert->isUserNotChanged($employee, $request)) {
                return $this->upsert->fill($employee, $request);
            }
    
            $employee->setUser($user);
    
            $this->isEmployeeExist($employee, $request);
            $this->upsert->isUserHired($employee, $request);
    
            return $this->upsert->fill($employee, $request);
        }
    
        public function isEmployeeExist(ManagementEmployee $employee, ManagementEmployeeRequest $request): void
        {
            if ($this->employeeRepository->findByManagementAndUser($employee->getManagement(), $employee->getUser())
            ) {
                throw new ManagementEmployeeAlreadyExistsException();
            }
        }
    }
  • Symfony Doctrine Коллекция. Как избежать дублирование записей в отношении (OneToMany - ManyToOne)?

    @sgidlev Автор вопроса
    tukreb, Мне показалась сложновата логика. Видимо нужно как-то правильно первичный ключ задать, чтобы метод contains в итоге срабатывал.
  • Symfony Doctrine Коллекция. Как избежать дублирование записей в отношении (OneToMany - ManyToOne)?

    @sgidlev Автор вопроса
    Исправил на такой вариант, по аналогии:

    В модели:
    /**
         * @param array|null $ownersId
         * @return $this
         */
        public function filterOwners(?array $ownersId): self
        {
            $criteria = Criteria::create()
                ->where(Criteria::expr()->in('id', $ownersId));
    
            $filteredEntries = $this->owners->matching($criteria);
    
            return $this->setOwners($filteredEntries);
        }
    
        /**
         * @return $this
         */
        public function clearOwners(): self
        {
            $this->getOwners()->clear();
    
            return $this;
        }
    
        /**
         * @param RoomOwner $owner
         * @return $this
         */
        public function addOwner(RoomOwner $owner): self
        {
            $roomOwnerExist = $this->getOwners()->exists(function ($key, RoomOwner $element) use ($owner) {
                return ($element->getOwner()->getId() === $owner->getOwner()->getId());
            });
    
            if (!$roomOwnerExist) {
                $this->owners[] = $owner;
                $owner->setRoom($this);
            }
    
            return $this;
        }


    И в самом сервисе:
    /**
         * @param array|null $ownersId
         * @param Room $room
         * @return void
         */
        public function addRoomOwners(?array $ownersId, Room $room): void
        {
            if ($ownersId) {
                $room->filterOwners($ownersId);
    
                foreach ($ownersId as $ownerId) {
                    $room->addOwner((new RoomOwner())
                        ->setTitle('Test Owner')
                        ->setOwner($this->ownerRepository->getById($ownerId)));
                }
            } else {
                $room->clearOwners();
            }
        }
  • Как получить измененный state из Pinia в Vue3?

    @sgidlev Автор вопроса
    Ну или сделать весь компонент асинхронным и использовать async/await синтаксис и поместить его в Suspense
    Про это вообще не знаю, нужно будет изучить.

    А за способ спасибо, рабочее решение.
  • Как использовать профайлер от Симфони для сбора метрики и отправки их в логгер?

    @sgidlev Автор вопроса
    Хотелось бы встроенную библиотеку использовать, Симфони и так неплохо данные собирает
  • Как использовать профайлер от Симфони для сбора метрики и отправки их в логгер?

    @sgidlev Автор вопроса
    Я немного догадываюсь, почему events пустой. Т.к. профайлер иницилизуется в текущий момент, события как таковые отсутствуют. Но они есть в Stopwatch-> activeSections -> и в массиве с ключом 0 нужные мне события.

    6287970c06643411133555.png
  • Как использовать профайлер от Симфони для сбора метрики и отправки их в логгер?

    @sgidlev Автор вопроса
    BoShurik, да, но и не только. Хотим для себя, как разработчики, также использовать. Смотреть как вырастает нагрузка при добавлении новых фич, проводить нагрузочное тестирование.
  • Как структурировать проект на Symfony по принципу package-by-feature?

    @sgidlev Автор вопроса
    Нашел вошлебную настройку для Контроллеров

    config/routes.yaml
    
    app_api:
      resource: "../src/*/Controller/*"
      type:     annotation


    И контроллеры теперь подцепляются по адресам:
    App/Balance/Controller/
    App/Order/Controller/
    ...

    Осталось похожее найти для Doctrine Entities
  • Как структурировать проект на Symfony по принципу package-by-feature?

    @sgidlev Автор вопроса
    Получается звездочка как рекурсия не работает, если ее добавить в путь.

    Ок, буду для каждой сущности прописывать свой путь
  • Как структурировать проект на Symfony по принципу package-by-feature?

    @sgidlev Автор вопроса
    Максим Максим Федоров Можете подсказать с конфигами на Симфони 5. Есть ли способ прописать в общем конфиге настройку таким образом, чтобы она рекурсивно проходилась по папкам и автоматом подцепляла контроллеры и сущности для миграций.

    Пробовал вот так с контроллерами, но не помогло:
    /config/services.yaml
    
    services:
        app_controller:
            namespace: App\
            resource: '../src/*/Controller'
            tags: ['controller.service_arguments']


    config/routes/annotations.yaml
    
    domain_controllers:
        resource: '../../src/*/Controller/'
        type: annotation


    Не хочется каждый раз лезть и создать новый конфиг или править общий.
  • Как структурировать проект на Symfony по принципу package-by-feature?

    @sgidlev Автор вопроса
    Максим, Ок, спасибо. Да, видимо придется попробовать несколько вариантов и выбрать какой-то оптимальный.
  • Как структурировать проект на Symfony по принципу package-by-feature?

    @sgidlev Автор вопроса
    Максим, Максим Федоров А можете кратенько структуру папок накидать и сюда выложить. Вообще не очень много реальных примеров по организации папок по Фиче, так, лишь немного поверхностно авторы проходятся по этой теме.
  • Как структурировать проект на Symfony по принципу package-by-feature?

    @sgidlev Автор вопроса
    Привет, Максим. Я как раз и пришел с доклада Удальцова.

    Ок, попробую и посмотрю, как будет получаться.
  • Как правильно сделать extend от entity модели в Doctrine?

    @sgidlev Автор вопроса
    Да, наверное так и поступлю.
  • Как правильно сделать extend от entity модели в Doctrine?

    @sgidlev Автор вопроса
    В идеальном случае я хотел бы, что доктрина вернула мне нужную модель с отформатированном виде. Сейчас я делаю мутацию модели через метод:
    public function fillData(Dislocation $dislocation): DislocationDownloadingData
        {
            $has = $dislocation->getVars();
            if ($has) {
                foreach ($has as $name => $value) {
                    $this->$name = $value;
                }
            }
    
            return $this;
        }
  • Как правильно сделать extend от entity модели в Doctrine?

    @sgidlev Автор вопроса
    sl0, Я явно хотел указать через РегистрМенеджер какую модель нужно дергать, но не сработало. А способ через dtype не подходит, т.к. мне всегда будет возвращаться модель Dislocation.
  • Как правильно сделать extend от entity модели в Doctrine?

    @sgidlev Автор вопроса
    Дмитрий, У меня в общем такая задача. Приходят данные и эти данные я записываю в таблицу, назовем их Дислокация.

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

    Поэтому пробую использовать некую абстракцию и наследование, но как-то воедино связать это с моделью Entity и doctrine не получается.
  • Как правильно сделать extend от entity модели в Doctrine?

    @sgidlev Автор вопроса
    В общем с возможностью использовать базовый/абстрактный класс я разобрался. После объявления аннотации вида:
    /**
     * @ORM\Entity
     * @ORM\InheritanceType("SINGLE_TABLE")
     * @ORM\DiscriminatorColumn(name="type", type="string", length=3)
     */


    Нужно создать, а затем накатить миграцию где в таблице появится поле type. В это поле потом записывается модель из аннотации @ORM\DiscriminatorMap

    А уже при селекте будет выбираться нужная модель исходя из записи в type.

    Но мне нужно по другому, мне просто найти способ переопределить модель, чтобы видоизменить методы вывода свойств.

    /**
     * @ORM\Entity
     */
    class Dislocation
    {
    	/**
    	 * @ORM\Id()
    	 * @ORM\GeneratedValue()
    	 * @ORM\Column(type="integer")
    	 */
    	protected $id;
    
    	/**
    	 * @ORM\Column(type="float", nullable=true)
    	 */
    	protected $timeInRun;
    
           public function getTimeInRun(): ?float
    	{
    	    return $this->timeInRun;
    	}
    }
    
    
    class DislocationDownloadingData extends Dislocation
    {
      public function getTimeInRun(): ?float
    	{
    		return round($this->timeInRun, 0);
    	}
    }