Ответы пользователя по тегу PHP
  • Как организовать и хранить конфиг по каждому пользователю, чтобы что-то делать один или несколько раз?

    dmitriylanets
    @dmitriylanets
    веб-разработчик
    я делал через вариант {par1: false, par2: true}

    Я конечно могу через запрос всем добавить другой объект со свойством par3 и новый функционал будет добавлен

    - не обязательно обновлять, если при получении нет свойства например view_modal у объекта значит просто он считается не просмотренный, при просмотре и подтверждении занесите свойство view_modal=true
    Ответ написан
    Комментировать
  • Как правильно создавать объект-сущность?

    dmitriylanets
    @dmitriylanets
    веб-разработчик
    на самом деле выглядит громоздко, но в этом есть ряд приимуществ.
    1. вы не зависите от слоя хранения, получения
    2. легко накатить тесты

    попробуйте организовать вашу логику и работу приложения не концентрируясь на внешнем апи.
    1. создайте сущности+интерфейсы+исключения
    2. создайте тестовый репозитрий реализующий метод получения но с хранением объектов прямо в репо.
    3. реализуйите логику работы с репозиториями и сущностями
    4. покройте код тестами используя тестовый репо, проверьте работу приложения
    5. реализуйте репозиторий работающий с внешним апи, переключите работу приложения на него.

    из приемуществ: тестируемый код , маштабируемость в будущем.
    Ответ написан
    Комментировать
  • Где проверять авторизацию пользователя?

    dmitriylanets
    @dmitriylanets
    веб-разработчик
    Иногда логика отображания может быть очень сложной, и вроде бы все просто есть контроллер, есть шаблон, есть данные. Подходов для решения может быть много, я бы выбрал следующий:
    1. Контроллер принимает запрос от пользозователя и обращается к сервису для получения контента
    2. Сервис контента, формирует контент но при этом проверяет права доступа, авторизиван не авторизован, дата,время, группа пользователя если атворизован, его пол и все что угодно. Отдает обратно контент согласно условиям.
    3. Контроллер получив контент передает в шаблон.
    Ответ написан
  • Достоверно известно, что некоторый API периодически отваливается по таймауту или с 5хх ошибкой, как решить?

    dmitriylanets
    @dmitriylanets
    веб-разработчик
    обработать ответ 500 ,подождать несколько секунд и попробовать снова, итак N попыток
    Ответ написан
    Комментировать
  • Какие инструменты использовать для разбора писем с помощью php?

    dmitriylanets
    @dmitriylanets
    веб-разработчик
    На самом деле все инструменты , их перечислю ниже, имеют свои недостатки, поэтому пришлось написать некий функционал явялющийся звеном для драйверов-библиотек, суть функционала :
    1. подключиться к серверу почты, получить N кол-во писем, скачать их содержимое, сохранить локально
    2. Прочитать письмо и пробразовать в ООП структуру, используя драйвер, в случае неудачи попробовать другой

    вот сообственно библиотеки который использовал:
    1. php_imap нативное php расширение, использовали в самом начале, не читало вложения некоторых писем, не работал imap_search для некторых серверов
    2. eden/mail - не читало некоторые вложения
    3. zend-mail (он же laminas-mail) - настроен на очень строгий rfc, если письмо отходит от стандарта кидает исключение.

    В текущйи момент остановились на измененном zend-mail драйвере но временами ловим письма которые не читаются, приходится адаптивровать драйвер.
    Ответ написан
    Комментировать
  • Правильно ли я делаю с точки зрения ООП?

    dmitriylanets
    @dmitriylanets
    веб-разработчик
    хелпер может существовать без данных, наверно вряд ли + метод получения ошибок
    тогда будет так
    $validation = new Validation($this->request->post());
    if(!$validation->validate()){
       //var_dump($validation->getErrors());
        return 'error';
    }


    про Helper молчу, вообще непонятно что делает
    Ответ написан
    Комментировать
  • Как создать модульность в mvc?

    dmitriylanets
    @dmitriylanets
    веб-разработчик
    хороший подход, использую очень давно, делается просто, я использую из твига такой вызов

    {{ module('Local.Slider',{speed:400}) }}

    или из php
    <?=module('Local.Slider',['speed'=>400]')?>

    Структура
    Local/Modules/Slider/Slider.php
    Local/Modules/Slider/tpl/default.twig

    тут нет модели. так как модель хранится чуть выше уровнем в пакете Local
    Ответ написан
    Комментировать
  • Правильно ли так организовывать код?

    dmitriylanets
    @dmitriylanets
    веб-разработчик
    1) из класса Partner должно идти как минимум одно исключение PartnerException
    все исключения в классе Partner будут "переварены в одно" PartnerException
    в вашем случае вы можете добавить еще наследника и PartnerNotFoundException
    логика становиться более лаконичной:
    //App\Services\Partner
    public function getSong(string $singer, string $song_name) : array
        {
            try {
                $json = file_get_contents('partner-domain.com/api?singer=' . $singer);
            } catch (\Exception $e) {
                throw new PartnerException($e->getMessage());
            }
    
            $songs = json_decode($json, true);
    
            //Filter songs by given name
            $result = array_filter($songs, function($song) use ($song_name) {
                return mb_strtolower($song['name'] === mb_strtolower($song_name));
            });
    
            //We need to return only 1 song
            if (!isset($result[0])) {
                  throw new PartnerNotFoundException(sprintf(
                       'parnter not found by %s and %s', $singer, $song_name
                ));
            } 
    
           return [
                    'status' => 'ok',
                    'data' => $result[0];
          ];
    }


    - в контроллере try catch если нужно поймать
    public function getSong(string $singer, string $song_name) : string
        {
             try{
                 $song = $this->partnerService->getSong($singer, $song_name);
             }
            catch(PartnerNotFoundException $ex){
    
                //п.с не помню как в лаевел но смысл поняли
                 return response()->status(404)->send();
            }
    
             return response()->json($song);
        }

    2) через конструктор
    3) аналогично п1
    4) метод называется getSong получить песню, это значит все альтернативные варианты загоняем в исключения, нет песни - исключение, ошибка сервиса, исключение
    Ответ написан
    Комментировать
  • Как лучше реализовать проверку данных в классе-сущности?

    dmitriylanets
    @dmitriylanets
    веб-разработчик
    а у вас не должно быть такого что заказ остался а покупателя удалили,
    это то же самое что удалить заказ а позиции заказа остались.

    при удалении покупателя нужно удалять все его заказы
    Ответ написан
  • Как организовать gitflow для библиотеки, подключаемой через composer?

    dmitriylanets
    @dmitriylanets
    веб-разработчик
    смотрите
    у вас в библиотеке два изменения
    вы ведь эти изменения делаете ветках? соотвественно
    Новая фича1 требует ветку dev-feature1 - указываете в компосере
    фича2 требует ветку dev-feature2 - указываете в компосере
    вот и все
    Ответ написан
  • Паттерн Фабрика или Фасад?

    dmitriylanets
    @dmitriylanets
    веб-разработчик
    Фабрика
    Ответ написан
    Комментировать
  • Codeception организация тестирования приемочных тестов?

    dmitriylanets
    @dmitriylanets
    веб-разработчик
    Да, верно, база должна быть эталонная, но не должна содержать пользовательских динамических данных, только дефолтные данные .
    во время тестирования туда будут загонятся тестовые данные, например регистрация пользователя и тд.
    при завершении тестов, база будет дропатся и тем самым не нужно будут удалять тестовые данные.
    п.с. Отлично подходит докер для этих целей , после тестов контейнер со всем данными удаляется.
    Ответ написан
    Комментировать
  • Как правильно перевести php в twig?

    dmitriylanets
    @dmitriylanets
    веб-разработчик
    $this->twig->render('template.twig',$arr)
    Ответ написан
    Комментировать
  • Best practice по константам проекта в PHP. Нормально ли собрать в файл все константы (100-200 шт) проекта?

    dmitriylanets
    @dmitriylanets
    веб-разработчик
    В драйвер БД встроена примочка для вставки констант и для неё приходится писать полный путь

    смотрите. при условии что драйвер для работы БД не может нормально работать с алиасами классов , вы пытаетесь подстроить архитектуру, а не исправить драйвер.
    Понимаете в чем дело , библиотеки которые вы используете формируют вашу архитектуру, библиотеки уйдут, заменятся другими, а архитектура останется.
    Ответ написан
    Комментировать
  • Как перейти к конкретной реализации при использовании принципа Dependency inversion?

    dmitriylanets
    @dmitriylanets
    веб-разработчик
    указывайте конкретику в phpdoc и будет вам счастье
    Ответ написан
  • Стоит ли использовать php фреймворк?

    dmitriylanets
    @dmitriylanets
    веб-разработчик
    Фреймворки приходят и уходят, когда то был популярен Codeigniter, теперь о нем забыли, сегодня Laravel завтра его ждет учесть того же Codeigniter, разработчики фреймворков не дают вам гарантии на большой промежуток времени. Но вы как программист даете гарантию при разработке и сопровождении.
    Если функционал надолго и с большой историей то symfony или ddd
    Если быстрый стартап с переписом в будущем то фраймворк Laravel, Yii или даже Codeigniter
    Если все что вы указали без доработок функционала то composer + пара пакетов
    Ответ написан
    Комментировать
  • MVC, ООП - какие должны быть сущности и связи, для такой задачи?

    dmitriylanets
    @dmitriylanets
    веб-разработчик
    понадобятся:
    Controller
    NewsService
    NewsRepository
    NewsEntity
    NewsCollection
    SearchCriteria

    1. Реализуем Domain
    Domains/News
    - Contracts/SearchCriteriaInterface.php имеет методы навигации, фильтров, сортировки getLimit, getPage, getFilterById и тд
    - Contracts/NewsRepositoryInterface.php имеет методы работы с хранилищем findById, findByCriteria и тд
    - NewsService.php , бизнес логика тут, создаем методы
    __construct(NewsRepositoryInterface $repository)
    getById(int $id): NewsEntity
    getBySearchCriteria(SearchCriteriaInterface $criteria): NewsCollection

    - NewsEntity.php , обычная DTO с сеттерами и геттерами (все что осталось от модели)
    - NewsCollection.php, коллекция содержащая список NewsEntity с методами интерфейсов Iterator,ArrayAccess,Countable

    2. Реализуем Application
    Application/Controllers
    - HomeController.php содержит методы списка новостей и одной новости, но моно создать два контроллера со списком и с детальной инфой, все на ваше усмотрение.
    __construct(NewService $service, Request $request)
    index() - метод основной или на ваше усмотрение, который получает коллекцию новостей пример
    $criteria = new \Infrastructure\Repositories\News\SearchCriteria;
    $criteria->setLimit(10);
     
    $newsCollection = $this->service->getBySearchCriteria($criteria);
    //далее куда угодно отправляем $newsCollection или в шаблон или в JsonResponce и тд

    - show(int $id), реализация страницы новости
    $newsEntity = $this->service->getById($id);

    3. Реализуем Infrastructure
    Infrastructure/Repositories/News/
    - SearchCriteria.php реализация интерфейса SearchCriteriaInterface
    - NewsRepository.php реализация интерфейса NewsRepositoryInterface

    3 этап на момент тестирования можно реализовать функционал не работая с источниками данных, то есть можно использовать заранее созданный сущности в специальных Mock репозиториях. Инжектить можно через DI, такая схема позволить покрыть код unit тестами
    Плюс такого подхода что код разрабатываем не от слоя хранения а от бизнес сущностей, SOLID и все такое
    Ответ написан
    2 комментария
  • Как правильно конфигурировать логгер и внедрять зависимость?

    dmitriylanets
    @dmitriylanets
    веб-разработчик
    5. внедрять логер в зависимости от интерфейса. В DI есть возможность вызвать метод setLogger в зависимости от интерфейса.
    Например вы создаете интерфейсы:
    FileLoggerAware extends LoggerAwareInterface 
    MysqlLoggerAware extends LoggerAwareInterface


    теперь конфигурирует DI
    $container->share(FileLogger::class,function(){
           return new NullLogger;//тут реализация file логера
    });
    $container->share(MysqlLogger::class,function(){
          return new NullLogger;//тут реализация Mysql логера
    });
    $container
        ->inflector(FileLoggerAware::class)
        ->invokeMethod('setLogger', [FileLogger::class]) 
    ;
    $container
        ->inflector(MysqlLoggerAware::class)
        ->invokeMethod('setLogger', [MysqlLogger::class]) 
    ;


    и все в нужном классе делаешь, (но все классы должны работать через awaring di, прилетать автоматом через конструктор)
    class MyClass implements FileLoggerAware
    {
    
    use LoggerAwareTrait;
    
    }


    ссылки:
    https://github.com/php-fig/log/blob/master/Psr/Log...
    https://container.thephpleague.com/3.x/inflectors/
    Ответ написан
    2 комментария
  • Приведите пример реализации MVC + Service Layer + Repository?

    dmitriylanets
    @dmitriylanets
    веб-разработчик
    Модель раскладывается на три составляющие: класс сущности (Entity), класс работы с бд(Repository) и класс для бизнес логики (Service)

    а пример простой
    <?php
    class Controller
    {
    
    	private $userService;
    
    	function __construct(UserService $userService)
    	{
    	
    		$this->userService = $userService;
    	}
    	
    	public function show(int $id){
    	
    		try{
    			$user = $this->userService->getById($id);
    		}
    		catch(UserNotFound $ex){
    			return new Response(null,404);
    		}
    	
    		return new JsonResponse($user->toArray(),200);
    	}
    	
    }
    
    class UserService
    {
    
    
    	private $userRepository;
    
    	function __construct(UserRepository $userRepository)
    	{
    	
    		$this->userRepository = $userRepository;
    	}
    
    	public function getById(int $id): User
    	{
    	
    		if(!$user = $this->userRepository->findById($id))
    		{
    			throw new UserNotFound($id);
    		}
    		
    		return $user;
    	}
    }
    Ответ написан