• Вопрос о тестах. Что вы об этом думаете?

    vabka
    @vabka
    Токсичный шарпист
    Проблема такого теста в том, что он тестирует сразу две вещи:
    1. Что работает api
    2. Что работает Model::factory()->make()->toArray(), которая генерирует аргументы для вызова api

    От этого возникают следующие проблемы:
    1. Без изменения кода api у тебя тест может неожиданно начать падать и наоборот.
    Например ты поменял названия параметров. Тест зелёный, тк фабрика генерит параметры, а клиенты все теперь 400-е ошибки получают.
    Контракт получается незафиксированный.
    2. Ты не можешь при таком подходе нормально проверить негативный сценарий и граничные значения.
    Чтобы их проверить - тебе всё также придётся отказаться от Model::factory()->make()->toArray() и составлять параметры руками.

    Вообще, api-тесты часто пишут вообще без привязки к коду - при помощи сценариев для postman например.
    Ответ написан
    Комментировать
  • Как в laravel построить сложную логику?

    iMedved2009
    @iMedved2009
    Не люблю людей
    Бизнес-логике - не место в контроллере.

    AppServiceProvider
    public function boot(){
    Validator::extend('count_books', function($attribute, $value, $parameters) {
            $count = IssueBook::where('client_id', $value)->where('is_returned', false)->count()
            return $count < 2;
    });
    }
    
    StoreRequest
    public function rules()
      {
        return [
          'category_id' => 'exists:App\Category,id',
          'client_id' => 'exists:App\Client,id|count_books',
          'book_id' => 'exists:App\Book,id',
          'days_issued' => 'nullable|string',
          'is_returned' => 'nullable|boolean',
          'return_date' => 'nullable|date',
        ];
      }
    Ответ написан
  • Как правильно выстраивать архитектуру метода контроллера на laravel?

    gzhegow
    @gzhegow
    aka "ОбнимиБизнесмена"
    Знакомая боль давняя.
    Принцип следующий - 1) исходные данные 2) превращение в обьект, 3) проверка (валидация) обьекта, 4) передача обьекта в какое-то действие, 5) проверка результата (если нужна) и 6) генерация респонса.

    Исходные данные. Смотрим что за фрейм. Лара. Умеет в инжекцию в метод контроллера и биндинг обьектов, если написать правильно. Можно сразу на вход писать MyActionRequest (сделать как в документации), в итоге первой строкой будет $myActionRequest->validate(), которая еще и выкинет 422 если что-то не так.

    В чем минус ларавельного реквеста? Он представляет собой как бы ВЕСЬ реквест. Это тоже самое что передавать $_GET по программе, что немного странно.

    Рекомендуется сделать свой обьект ДТО. Это по сути твой же реквест, только в нем поля все заранее описаны, голый класс без методов - одни свойства. После валидации ты свои данные из GET/POST/LaravelRequest ручками втыкаешь в поля ДТО и этот заполненный ДТО можешь дальше кидать как "точно проверенная пачка данных" и не боятся каждый раз "а что если что-то не так" - оно точно так, потому что ты уже проверял.

    Попадать в какой-то другой класс ДТО может целиком если это кусок бизнес-части программы или по одному полю / массиву полей если это какой-то кусок ядра или глобалки. Пример ядро - класс Money, который переводит там валюты знач и позволяет их переключать. Туда ваш MyActionDTO будет странно кидать, а вот string $money или PHPMoney/Money- пжалста. А вот в обьект например MyCatalog кинуть MyActionDto уже не так уже и плохо.

    Вот это ваше действие может быть просто SQL запросом. Category::query()->find() это тоже действие, а почему нет. Рекомендуют конечно запросы выбрасывать в "тип классов" (папка, такая же как "модели") репозиторий чтоб потом когда поломается чего не везде править а в одном месте, но тут тоже, пока помните, что где можно не париться, и потом - увлечетесь обобщением поймаете прикол когда не понятно какую половину запроса в контроллер, а какую - в репозиторий. Стоит помнить что мы пишем задачу под задание, задание представляет собой контроллер, а остальной код ему помогает. Поэтому частности в экшон, похожести - в отдельный класс.

    Ну так вот, это "действие" что-то вам вернет. Может это готовый к выводу контент, а может еще какие-то данные которые для вашего экшена может кривые. Вот проверяете "оно? или нет". Частный случай - ничего в базе не нашлось, это критично, и 404 или просто выплюнуть "нет записей" и 200? Проверили ифами, сделали респонсы - где 404ый, где двухсотый, где джсоновский, где иксмэльный, файл-довнлоадный или стримовый.

    Потом пошли в Handler.php написали отлов исключений в целом по программе и их запись куда-то, в базу, в кеш или на удаленный логгер-анализатор, чтобы везде не писать try catch а просто писать throw и знать что оно "хоть как-то но отрисуется" и куда надо запишется.

    public function destroy(Request $request, $id) // или MyDestroyRequest если юзер что-то еще передает
        {
            $request->validate(); // оно под капотом ещё и авторизацию проверит и 422 само выбросит
    
            // ... верно тут можно проверять политику, но в принципе если реквест наследовать то в ларе помоему их можно и в реквест код перенести и первый метод еще и это сделает
          
            $dto = new MyDestroyDto($request->valid()); // там внутри пишем что куда, хотя можно и здесь
    
            $myDomainService = new MyDomainService(); // или new MyCoreService();
            $status = $myDomainService->doSomething($dto); // или $result = $myCoreService->convert($dto->money);
    
            if ($status === 'ok') return $this->jsonDone('@api.msg.ok_message', 200); // вот метод jsonDone в базовом (абстрактном*) контроллере или, например, в трейте, напиши
            return $this->jsonFail('@api.err.error_message', 500); // собаки в тексте для наглядности, трехуровневый ключ перевода из файла lang, по собаке просто можно определить текст уже проходил через переводчик или нет. три уровня нужны, чтобы когда много переводов скапливается их можно было подключать частями. Первый уровень - где подключать (api, catalog, social), второй - что (msg, err, label, placeholder, title, h1)
        }
    Ответ написан
    1 комментарий
  • Как поступить с заказчиком?

    SeaInside
    @SeaInside
    15 лет пилю все эти штуки
    Вы ведь сами понимаете, что это ваш косяк, а не заказчика? А чего ему за него платить?

    Мне прям вспомнилась ситуация, когда я пришёл к стоматологу вылечить кариес (пусть будет 2 000 рублей), мне рассверлили весь зуб и заявляют "батюшки, да тут же пульпит!" (12 000 рублей).
    Помогите Маше найти фразу для стоматолога, которая позволила бы сохранить хорошие отношения.

    И вот вы сейчас, по прошествии 2 месяцев, оставите заказчика один на один с незавершённым проектом, в котором никто не разберётся (то есть по сути ни с чем) - то вы поступаете как чудак. Я же вижу по тексту, что понимаете.
    А сюда вы за индульгенцией пришли? :)

    Хотите "сохранить отношения" - доделывайте как есть, в будущем будете мудрее.

    Если совсем невмоготу - возвращаете оплату целиком и сильно извиняетесь (можно даже сверху накинуть).
    В таком случае о хороших отношениях говорить не приходится, но послевкусие хотя бы как от порядочного человека.

    Два совета на будущее:
    1) Перезакладывать стоимость и время разработки;
    2) Если понимаешь, что всё идёт не по плану/пропал интерес / слишком сложно / некомфортно / whatever - сказать об этом сразу и отказаться от проекта, так всегда получается честнее

    Опыт всегда стоит дорого, а 53 000 рублей - ну не самая большая цена его получения :)
    Ответ написан
    8 комментариев
  • Как задать пути к файлам на сервере?

    alexey-m-ukolov
    @alexey-m-ukolov Куратор тега Laravel
    Подобные проблемы решаются в принципе корректной настойкой сервера.
    Ответ написан
    Комментировать
  • Три цены, одна дефолтная, 2 зависят от чекбоксов, как все плюсовать?

    Rsa97
    @Rsa97
    Для правильного вопроса надо знать половину ответа
    2 комментария
  • Я сделал Code Review, может быть я где-то ошибся или у вас есть что добавить?

    @Sing303
    Опишу, как бы комментировал я
    public sealed class DataProvider : IDisposable
    {
        // nit: Предложил бы названия firstValue, secondValue либо более осмысленные, если возможно
        public extern int LongRunningCalculation(int value, int value2);
        public extern void Dispose();
    }
    
    // nit: сразу бы хотелось видеть уровень доступа и sealed (если класс не планируется наследовать)
    // Class2 - дать нормальное имя
    // { - перенести на 2ю строку по рекомендациям code style от microsoft (если не принято иных)
    class Class2 {
        // Синхронизация не нужна, если убрать метод Init, а Create вызвать в статическом конструкторе
        private readonly object _sync = new object();
        
        // _ht - дать осмысленное название
        // Судя по использованию, value может быть int`ом. Не зачем иметь лишний boxing и проверки на тип
        // _ht статический, значит к нему могут быть обращения из разных потоков, лучше сделать его ConcurrentDictionary
        // Прям сходу не могу сказать, но, возможно, использовал бы какой то другой тип Dictionary <key, key, val> (самописный или существующий), кажется, так было бы быстрее чем массив в ключе
        private static Dictionary<int[], object> _ht; 
    
        // nit: хотелось бы имена со смыслом
        public int GetValue(int index, int index2)
        {
            // Лишний метод, удалить. Create вызовем в static конструкторе
            Init();
            // Если ключ у нас объект, то необходимо реализовать IEqualityComparer для этого Dictionary (иначе не понятно как по нему искать)
            var key = new[] {index, index2};
            // Проверка на тип не нужна, Dictionary сделаем типа int
            if (_ht.ContainsKey(key) & _ht[key].GetType() == typeof(int))
                // приведение типов больше не нужно
                return ((int)_ht[key]);
            // nit: else не обязателен
            else
                // int не может быть null, будет ошибка, вернуть либо default, либо возвращаемое значение должно быть int?
                return null;
        }
    
        // Метод удалить, вызовем Create в статическом конструкторе без lock
        public void Init() 
        {
            if (_ht == null)
                lock (_sync)
                    Create();
        }
        
        // Нет смысла делать метод public, сделать private
        public static void Create() 
        {
            // nit: и так видно какой тип создаём, можно использовать var
            // Обернуть в using
            DataProvider provider = new DataProvider();
            
            // Тут следует инициализировать значение _ht, т.к. ранее оно нигде не создаётся
            // Не забыть передать реализацию IEqualityComparer в конструктор
            
            // nit: хотелось бы видеть использование фигурных скобок (если не принят иной code style)
            // nit: вместо int можно var
            // i и j, похоже, несут какой то смысл, можно попробовать придумать нормальное название (иначе не понятно почему 100 и 12, их можно в константы класса)
            // nit: возможно можно использовать Parallel.ForEach
            for (int i = 0; i < 100; i++)
                for (int j = 1; j <= 12; j++)
                    _ht[new [] { i, j }] = provider.LongRunningCalculation(i, j);
        }
    }

    А переписал бы так (если не убирать массив в dictionary)
    public interface IDataProvider : IDisposable
    {
        int LongRunningCalculation(int firstValue, int secondValue);
    }
    
    public sealed class DataProvider : IDataProvider
    {
        public extern int LongRunningCalculation(int firstValue, int secondValue);
        public extern void Dispose();
    }
    
    public sealed class DataProviderService
    {
        public DataProviderService(IDataProvider dataProvider)
        {
            _dataProvider = dataProvider;
        }
    
        private static readonly ConcurrentDictionary<int[], int?> _calculatedCache = new ConcurrentDictionary<int[], int?>(new CalculatedEqualityComparer());
        private readonly IDataProvider _dataProvider;
    
        public int? GetValue(int firstValue, int secondValue)
        {
            var isNotSupportedValues = firstValue > 100 || firstValue < 0 || secondValue < 1 || secondValue > 12;
            if (isNotSupportedValues)
            {
                return null;
            }
    
            var key = new[] { firstValue, secondValue };
            if (!_calculatedCache.TryGetValue(key, out var result))
            {
                result = _dataProvider.LongRunningCalculation(firstValue, secondValue);
                _calculatedCache.TryAdd(key, result);
            }
            
            return result;
        }
    }
    Ответ написан
    5 комментариев
  • Как понять микросервисы?

    @deliro
    Как понять микросервисы?

    Прочитать соответствующую книгу (а лучше ещё парочку про DDD или хотя бы посмотреть этот доклад)

    Затем ответить на несколько вопросов:
    1. Почему вы решили, что микросервисы что-то вам дадут?
    2. Есть ли у вас настоящие причины для микросервисной архитектуры? (А именно: зоопарк технологий с невозможностью написать 99% на одном языке; более тысячи разработчиков; сложность выкатки монолита в виде часов прогонов CI/CD — тестов, билда, деплоя, стопоров выкатки в виде кучи проблем из-за разработчиков; вы такие же большие как гугл, убер, амазон и т.п.). Или вам просто нравится модное слово "микросервисы"?

    Не получится создать хорошую микросервисную архитектуру без умения создать хороший модульный монолит. В этом случае вы получите не только все проблемы плохого монолита: высокая связанность, каскадные падения, долгий CI/CD; но и все проблемы микросервисов: их надо оркестрировать (у вас же есть команда, которая будет поддерживать инфраструктуру?); каждому микросервису нужно своё CI/CD (и хорошее); сеть может (и будет) лагать и обрываться; длительность запросов увеличится на порядок(ки) (особенно если выбрать какой-нибудь JSON-RPC over HTTP); нужно предусмотреть failover strategy (например, идемпотентные ретраи. Вы же уже знаете про correlation id, саги и что делать, если прилетел network error на запрос в другой сервис "списать 10 баксов"?) и circuit breakers; трейсы и логи, которые не пришлось бы искать по сотням .log файлов от каждого сервиса; бизнес-логика расползётся по разным микросервисам и нарушит SRP (пофиг, что нарушит, важнее то, что это починить будет сильно сложнее). Список можно продолжать долго.
    Ответ написан
    11 комментариев
  • Есть ли шанс стать востребованным js, html/css программистом на фрилансе учитывая огромную конкуренцию?

    approximate_solution
    @approximate_solution
    JS Developer. Angular\React\Vue\Ember
    На правильном ли я пути, что учу javascript в плане карьеры?

    Если в компанию - то да, если фриланс - нет.

    Смогу ли я уверенно выполнять заказы на фрилансе выучив javascript?

    Нет, сам javascript никто не использует(имеется ввиду не верстка типовых лендосов, которые стоят 3 копейки, а более сложных проектов в виде SPA), используют over1000+ библиотек и решений которые крутят js под капотом. Что бы их валидно применять к конкретной бизнес задаче - нужен уровень не ниже мидла как разработчика, и уровень не ниже менеджера средней руки - что бы всё это продать за хорошую цену.

    И стать востребованным?

    Учиться 2-3 года, желательно после 1го года учебы максимально быстро попасть на: а) стажировку, б) работу.
    Ответ написан
  • Почему не работает whereHas?

    New_Horizons
    @New_Horizons Автор вопроса
    Бред:
    $products = Product::with('gifts')
    	->where('is_gift', '=', false)
    	->whereHas('gifts', function (Builder $query) {
    		$table = $query->getModel()->getTable();
    		$query->where("{$table}.id", '=', 4);
    	})
    	->get();
    Ответ написан
    3 комментария
  • В чем ключевое ПРАКТИЧЕСКОЕ отличие статических переменных и методов от обычных? Просто более компактный код?

    Adamos
    @Adamos
    class Demo {
      static private $db = null;
      static private $cache = [];
    
      static protected function getDb() {
        if(self::$db === null) {
          self::$db = ... // init $db
        }
        return self::$db;
      }
    
      static protected function cachedAction($arg) {
        if(!array_key_exist($arg, self::$cache)) {
          self::$cache[$arg] = ... // do action
        }
        return self::$cache[$arg];
      }
    }
    
    class DemoChild : public Demo  {
    }
    
    $a = new Demo();
    $b = new DemoChild();
    $a->getDb()->query();
    $b->getDb()->query(); // uses the same connection
    $c = $a->cachedAction('one');
    $d = $b->cachedAction('one'); // no action, cache used
    Ответ написан
    1 комментарий
  • Как задать маршрут в laravel, если кол-во опций неизвестно?

    solotony
    @solotony
    покоряю пик Балмера
    я явно формировал маршрут /a/b/c/ для "пост" и записывал его в бд.
    потом все "непонятные" URL просто проверял по б/д
    Ответ написан
    2 комментария
  • Как программисту отдыхать и организовать распорядок дня?

    На удаленку. 4 часа в день без выходных выдавать нагрузки по задачам. Будет 120 часов в месяц, офисная норма. Устать не возможно.
    Ответ написан
    Комментировать
  • Как программисту отдыхать и организовать распорядок дня?

    @podde
    самый младший сисадмин
    Не вините себя. У вас объективно очень много времени уходит на дорогу.
    Совершенно серьёзно думаю, что в такой ситуации надо попробовать поговорить с руководителем на предмет хотя бы частичной (пару раз в неделю) удалённой работы. А лучше попробовать вообще выпросить удалённый режим.
    Если нет – пробовать искать другую работу (поближе к дому или удалённую).
    Ну, действительно, по пять часов в день на дорогу – это не дело.
    Ответ написан
    Комментировать
  • В сфере IT для новичков нет перспектив?

    @nApoBo3
    Это сложный вопрос. На первый взгляд на низовых позициях в ИТ дикая конкуренция. С другой стороны, она дикая но совершенно беззубая, поскольку уровень кандидатов соответствует дворнику.
    Месяц назад искал себе помощника помощника, кандидатов море, уровень ниже плинтуса, не знают ничего совсем, две кнопки выучили и вот уже ИТ специалист.
    Работа, это все таки не школа и не институт, тут вы в замен тоже что-то должны дать, т.е. должны делать какую-то работу полностью самостоятельно и с должным качеством. Если вы что-то умеете делать самостоятельно и полностью понимаете процесс, то вот такую работу и ищите. То что кто-то может next, next, finish, поставить windows ИТ специалистом данного человека не делает.

    Вот и получается, конкуренция дикая, но среди перспективных ее практически нет.
    А перспективные это те, кто понимают, чтобы получить нужно сначала вложить, и много, и регулярно вкладывают в себя.
    Ответ написан
    3 комментария
  • В сфере IT для новичков нет перспектив?

    prrrrrrr
    @prrrrrrr
    Верстаю сразу на PHP.
    ...а требования (по знаниям и опыту) как к высококвалифицированному специалисту

    всё правильно. помощник для того чтобы помогать программисту делать то, что делает он, и использовать те технологии которые использует он.
    у меня есть тоже знакомый который постоянно проситься ко мне "помощником" за копейки, но нафиг он мне нужен если он в программировании 0. в чём он сможет мне помогать? отвлекать?

    Ну как начать работу, если никуда не берут

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

    FanatPHP
    @FanatPHP
    Чебуратор тега РНР
    Хайлоад

    Сразу поймёшь, что ничего не знаешь вообще
    Ответ написан
    2 комментария
  • Как сделать две формы регистрации для разных типов клиентов в laravel 7?

    qant
    @qant
    programer
    Да уж... вам явно рановато ларавел изучать ) если такие базовые вещи заставляют писать такой вопрос...
    Задавать конечно вопросы не стыдно и даже полезно, это нормально на старте, но причем тут Ларавел 7? Вам нужно изучить базу php. Желательно с учетом новых версий php-7+

    Далее нужно просто изучить несколько примеров "Как создать форму php" "как отправить форму php" "как отправить форму php ajax"

    Изучив разные примеры, вы поймете принцип, как работает отправка формы из html и этот вопрос отпадет сам собой
    И вы поймете что количество форм на странице может быть любым, более того, одну и ту же форму можно использовать для регистрации и пользователя и администратора добавив всего один параметр (например роль, который будет использоватся для проверки в коде)
    Ответ написан
    Комментировать
  • Laravel структура контроллеров?

    glaphire
    @glaphire
    PHP developer
    Фронтенд, личный кабинет и админка - разные по логике модули, для независимого развития кода больше всего подходит пункт 1. Общую логику всегда можно вынести в сервисы/хелперы и через DI передавать контроллерам
    Ответ написан
    8 комментариев