• Как прекратить создавать объекты классов?

    Во втором случае можно класс Database менять на что угодно, но согласно интерфейса.
    За счёт DI объект будет создан сам. Но можно передать нечто ручками. По этому можем тестировать имитируя Database. (Для фриланса и плюшевых проектов TDD в игноре, но всё же актуален.)

    Даже для плюшевых проектов порой смена Database нужна. Впрочем у меня был доступ не к БД, а к сайту через запрос xml файла с парсингом айтемов. Сделал в виде репозитория. Далее через пару месяцев сервис отдававший (кривой xml) создал API. Правка репозитория оказалась быстрой.

    А ещё через неделю потребовалось для других URL другой сервис, но в те же айтемы. Другой репозиторий. Получаем его через фабрику, которая отдаёт и первый, но зависимо от URL ресурса. Часть которая работала с самими айтемами не изменилась нисколько.
  • Какие есть курсы для php разработчика среднего уровня?

    Жесть Елисеев. Много шума. Именно много долго продолжающегося шума. Гремит. Шумит. Трендит. Много времени забирает. По крайней мере в недели ООП. А на самом деле всё это могло бы уложится в пару-тройку часов (не нужно объяснять растянутость низким уровнем кого-то, им это бесполезно и через 200 часов). Рассказать о простых вещах, о которых я читал ещё в 80-х, 90-х и начале 2К. Пространство имён, типизация, структуры. А как без этого люди приходят к ООП? Оно для них бесполезно.

    DRY, KISS... Может лучше SOLID и далее DDD, как попытка его применения? :)
    Паттерны - узко и конкретно по реализации.

    И практика, практика, практика.
    Первое. Код не пишется с нуля по спецификации.
    Второе. Код не пишется, а улучшается по мере прозрения заказчика и разработчика.
    Третье. Места улучшения - это как раз то, что основывается на SOLID.
    Четвёртое. DDD - попытка объять не объятое.
    И контрольный: Текущий код - это результат. Чем больше векторов сознания влияет на изменения многих участков кода, тем более он бесполезен. Но нет смысла ругать векторы сознания. Им нужно либо увеличивать область влияния, либо уменьшать. Но при этом учитывать изменения проявления вектора согласно внешней среды.

    Простите, если пафосно. Да. Вообще - ООП и функциональное - это о внесении изменений во внесение изменений без нарушения изменений внесения изменений внесённых ранее.
  • Стоит ли новичку начинать с фреймворка или лучше учиться на чистом php?

    Одно и то же с точки зрения ежедневной работы. Достанет. В ларавель приятней. В битрикс сытней. Но всё это страдания.
  • Стоит ли новичку начинать с фреймворка или лучше учиться на чистом php?

    Отчего же? Всегда приятно с хорошим человеком поговорить.
  • Стоит ли новичку начинать с фреймворка или лучше учиться на чистом php?

    Во как все на вакансии присели. А слабо самим заказчиков искать и местечковые сайты лабать на ларе?
    Понятно, что либо программист, либо бизнесмен. Но всё же просветлённый над ними всеми :)
  • Каким должен быть правильный контроллер?

    Александр Шаповал: ещё есть книга Domain-Driven Design in PHP. Раньше она была издана от Leanpub, а недавно вышла от Packt Publishing (на пару с Apress стали перетягивать книги у Leanpub). В интернете можно найти пиратские версии и старого и нового издания.
  • Каким должен быть правильный контроллер?

    Как я вижу, тут пересеклись те кто знают о DDD и те кому достаточно MVC :)
  • Как правильно спроектировать Laravel приложение с уклоном в enterprise?

    Kir ---:
    По существу моего вопроса вы не ответили. Получение квери билдера в контексте нужной модели без создания (ненужного) экземпляра модели - вполне норма. В данной ситуации и в других вызов статических методов оправдан.

    Остальной текст является сплошным "бла бла бла", так как не имеет примеров в контексте laravel. Смысл что-то ещё обсуждать?

    И да
  • Как правильно спроектировать Laravel приложение с уклоном в enterprise?

    Kir ---:
    Мне не понятно ваше отношение к статическим методам. Можете объяснить?
    Других аргументов я с вашей стороны не увидел. Только не знание laravel и фантазии по этому поводу.
  • Как правильно спроектировать Laravel приложение с уклоном в enterprise?

    Kir ---:

    Вы уже из-за подхода Laravel начали во всю использовать статики: Card::change Log::error

    Давайте отделим мух от котлет. Один метод мой и он не обязательно должен быть статичным, но при простой реализации он сразу скрыл всю кухню поиска нужной корзины и её изменений. Это же указано в комментарии: Изменение корзины внутреннее дело самой корзины

    Ниже я изменил реализацию с учётом ваших желаний. Простая реализация с привязкой к сессии. Кроме того добавил немного кода модели.

    class CartController extends Controller
    {
        //...
        public function change($productId, $action)
        {
            try {
                $session_id = session()->getId();
    
                $cart = Cart::bySession($session_id)
                    ->firstOrCreate(['session' => $session_id]);
    
                $cart->change($productId, $action);
                
            } catch(\Exception $e) {
                // Здесь можно разрешить как не стандартные ситуации, так и стандартные.
                // Но у нас пока одно.
                Log::error('Cart change: ' . $e->getMessage(), [$productId, $action]);
    
                return back()
                    ->with('message', 'Простите, дяденька, засранца.');
            }
    
            // Возвращаемся туда от куда добавился товар или изменилось его количество
            return back();
        }
    }


    namespace App;
    
    use Illuminate\Database\Eloquent\Model;
    
    use App\Cart\ActionException;
    
    class Cart extends Model
    {
        // ...
        public function scopeBySession($query, $sessionId)
        {
            return $query->where('session', '=', $sessionId);
        }
    
        public function change($productId, $action)
        {
            switch($action) {
                case 'append':
                    $this->processAppend($productId);
                    break;
                case 'reduce':
                    $this->processReduce($productId);
                    break;
                case 'clear':
                    $this->processClear($productId);
                    break;
                default:
                    throw new ActionException($action, $this->session, $productId);
            }
        }
    
        private function processAppend($productId)
        {
            // ...
        }
        // ...
    }


    Это абсолютно не рашсряемо. Если у вам нужно подменить логгер в зависимости от окружения. Задача, кстати, очень частая, когда подменяют именно логер.


    С логированием всё просто. Зачем порождать для записи в лог объект и потом обращаться к единственному его методу. Но именно так вы можете и делать, если хотите. Ларавелевский же логер - это обёртка над monolog. Вы можете использовать monolog напрямую. И не вижу проблем со сменой логера. Для любого можно сделать обёртку аля ларавелевская (какой там паттерн?).


    Card::change - вы жестко завязались на метод change который что-то делает.

    Проблема целостности логики приложения — тут не видно ни связи текущего пользователя с корзиной. Потенциальная уязвимость того, что вы можете привязать товар ни к той корзине. Если один экшен, это не так критично. А представьте, если таких мест десятки. Как пример амазон. Там различных способов работы с корзиной кучи.

    Проблема расширяемости. Вы не можете подменить спокойно реализацию корзины для разных пользователей. Например, если у юриков и у физиков разные способы размещения заказов в ней (если правильно помню, тоже из амзмона пример)


    Все методы что-то делают. Кроме того, по названию видно именно что делает мой метод. Это для простого решения.
    Впрочем, выше я привёл реализацию с учётом сессии. Никто вам не мешает создать фабрику, получающую ряд параметров, и возвращающую нужную корзину. Это не проблема фреймвока. В его контексте нет корзины. Другими словами подвожу итог, что вы ругаете фреймвок за то, за что он не отвечает.

    Теперь закончим насчёт статики. Первый вызов статического метода типа where() и т.д. в контексте Eloquent позволяет вызвать квери билдер в контексте модели. Так реализован Eloquent. Не нравится? Используйте привычную вам реализацию. Можете обратится к Doctrine.

    Опять же, мы подошли к тому, что проблема не во фреймвоке, а умении им пользоваться.

    Вы, скажете, что это не проблема Laravel. А я скажу, что именно его. Потому что он не предоставляет своей методики, как правильно получать экземпляры, кроме как через запрос в контроллере через статические методы подключения к БД.

    Ответил выше. Нет никакого обращения к БД в данном контексте. Только к построителю запроса для модели. К какой БД обращается модель можно указать в самой модели (если их несколько в приложении). Контроллер ничего не знает о БД. И не должен знать.


    Далее. Как вы собираетесь прикручивать ко всему этому гибкие права. Например, у пользователя с ролью менеджера есть права только на чтение.

    Можем использовать систему полиси самого ларавеля. Уж извините, но я не буду пересказывать вам документацию.
    Можем взять любую другую реализацию. Их много.
    И точек для внедрения хватает. Всё зависит от контекста бизнес задачи.
    class CartChange extends FormRequest
    {
        // НАПРИМЕР, ОДНА ИЗ ТОЧЕК ПРОВЕРКИ ПРАВ!!!
        public function authorize()
        {
            return true; // Если в данном контексте мы этим не замарачиваемся
        }

    Теперь возвращаемся к коду. В CartChange много чего завязано на какие то ассоциативные массивы и куча публичных методов. Я не понимаю как это работает. Это вообще процедурное программирование. Тут нет реализации каких то интерфейсов, для понимания, что можно передать, а чего нельзя.


    Если знаете laravel и посмотрите на строчку use App\Http\Requests\CartChange; в контроллере, то всё становится понятно. Это обработчик одного запроса, валидирующий данные запроса. А если посмотреть в код, то видим, пусть и магическую, но вполне читаемую реализацию на основе встроенных валидаторов. Можно писать свои.
    Сам FormRequest от которого мы наследовали CartChange имеет свой интерфейс. Впрочем и его вы можете расширить, если вам нужно.

    В контроллере я передаю объект CartChange в метод обрабатывающий запрос. Если валидация не прошла, то до метода контроллера и дела не дойдёт. По умолчанию будет редирект на url с которого был запрос и вернутся сообщения об ошибках (в случае с Ajax будет просто возврат ошибок в json).

    И что теперь стоит опять отметить (вы и сами это признали), то что вы не знаете laravel, но упорно пытаетесь его облажать.

    Это сплошное нарушение SOLID, так как CartChange нарушает принцип единой ответственности. Он предоставляет различные данные разной с разной природой.

    Вы сами себе противоречите. CartChange - это единственное место проверки данных единственного запроса. Для каждого запроса вы можете создать свой класс. Природа же данных запроса всегда одна - это текст из HTTP.

    Даже передавать разный request для каждого action попахивает. Меняем строчку в роутинге и вам приходит другой объект. Потенциальное место огрести кучу неявных ошибок.

    Вы можете использовать CartChange для нескольких разных запросов.
    И если вы не заметили, то роуты именованные. Меняйте всё, кроме имён параметров. Не используйте имена параметров в URL без нужды. Всё в руках разработчика.

    <!-- Вариант с параметрами в URL -->
    <form action={{ route('cart.change', ['action' => 'append', 'product' => $produt->id]) }} method="post">
        {{ csrf_field() }}
        <button type="submit">+</button>
    </form>
    <span>{{ $cart->productQuantity($produt->id) }}</span>
    <form action={{ route('cart.change', ['action' => 'reduce', 'product' => $produt->id]) }} method="post">
        {{ csrf_field() }}
        <button type="submit">-</button>
    </form>
    
    <!-- Вариант с CartChange Параметры в форме  -->
    
    <form action={{ route('cart.change') }} method="post">
        {{ csrf_field() }}
        <input type="hidden" name="action" value="append"> 
        <input type="hidden" name="product" value="{{ $produt->id }}"> 
        <button type="submit">+</button>
    </form>
    <span>{{ $cart->productQuantity($produt->id) }}</span>
    <form action={{ route('cart.change']) }} method="post">
        {{ csrf_field() }}
        <input type="hidden" name="action" value="reduce"> 
        <input type="hidden" name="product" value="{{ $produt->id }}"> 
        <button type="submit">-</button>
    </form>


    Резюмирую. Laravel вполне годен для энтерпрайза, если "умеешь готовить". Но это "умеешь готовить" ещё более важно для Symfony. Решения на Laravel можно добиться быстрее. При этом все зависит, как и всегда, от разработчиков.

    Бизнесу нужны быстрые решения. Для этого нужно искать золотую середину, а не быть снобом.
  • Как правильно спроектировать Laravel приложение с уклоном в enterprise?

    Вы вообще на что это пишите? Я вас спрашиваю, поверх чего создаются слои и для чего.

    Так как раз запросы по DDD и будут над ActiveRecord. А там ещё могут быть слои связанные с другими айтемами.

    Специально внёс изменения в код выше, для понимания:

    // ...
    Route::post('cart', [
        'as'   => 'cart.change',
        'uses' => 'CartController@change',
    ]);


    <?php namespace App\Http\Controllers;
    
    // Для папки App\Http\Requests\ валидируемые запросы идут из коробки, 
    // но требуют определения
    // Например, реализуем проверку параметров сортировки
    use App\Http\Requests\CartGet; 
    // Здесь проверяем product_id и action
    use App\Http\Requests\CartChange; 
    
    // Наши реализации для DDD, которые работают с ActiveRecord
    use App\Domain\Cart\GetRequest;
    use App\Domain\Cart\ChangeRequest;
    
    use Illuminate\Support\Facades\Log;
    
    class CartController extends Controller
    {
        public function show(CartGet $request)
        {
            $cart_request = new GetRequest($request);
            $cart = $cart_request->process();
    
            return view('cart.show')
                ->with('cart', $cart);
        }
    
        public function change(CartChange $request)
        {
            try {
                $cart_request = new ChangeRequest($request);
                $cart_request->process();
            } catch(\Exception $e) {
                // Здесь можно разрешить как не стандартные ситуации, так и стандартные.
                // Но у нас пока одно.
                Log::error('Cart change: ' . $e->getMessage(), $request);
    
                return back()
                    ->with('message', 'Простите, дяденька, засранца.');
            }
    
            // Возвращаемся туда от куда добавился товар или изменилось его количество
            return back();
        }
    }


    И валидация данных формы из коробки. Я добавил файл, но содержимое его уже работает.
    Свои валидаторы по вкусу то же можно добавить.
    <?php namespace App\Http\Requests;
    
    use Illuminate\Foundation\Http\FormRequest;
    
    class CartChange extends FormRequest
    {
        public function authorize()
        {
            return true;
        }
    
        public function rules()
        {
            return [
                'product_id' => 'required|exists:products,id', 
                'action'     => 'required|in:append|reduce|clear',
            ];
        }
    
        public function messages()
        {
            return [
                'product_id.required' => 'Не выбран товар.',
                'product_id.exists'   => 'Товар не доступен.',
                'action.required'     => 'Не выбрано действие над товаром',
                'action.in'           => 'Не верное действие над товаром',
            ];
        }
    }


    Вообщем я добавил обработку реквестов доступа к элементам домена. Улучшил тестируемость.

    Если считаете валидацию волидацию отдельно недопустимой и готовы её перенести в реквесты домена (и так и нужно, если не вестись на фреймвок), то опять же, легко можно передать в метод контроллера вместо CartChange $request обычный Request $request.

    И конечно же присутствует в этом всём магия, как в любом фреймвоке. И она минимальна и прозрачна.

    Вы хоть раз принимали участие в большой разработке?

    Вы правы, очень мало. Я фрилансер из далёкой провинции. Но даже мне понятно, что правка кода в PHP не должна влиять на целостность БД. Может повлиять на качество данных в какой-то мере. Но это не одно и то же.
  • Как правильно спроектировать Laravel приложение с уклоном в enterprise?

    Kir --- Я посмотрел приведённый вами пример. Контроллер в laravel не должен быть таким. Просто это пост начинающего ларовельщика. (Например он не умеет пользоваться именованными роутами и дествия над объектом выполняет через GET запросы.)

    Весь метод контроллера можно разбить на уровне роутов (смешаны HTTP методы и действия).

    Реквесты по каждому методу можно оформить свои с валидацией и передавать их сразу в метод контроллера.

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

    По мне можно для любого инструмента не верные образцы использования и манипулировать ими. Другими словами: это не кот невкусный - это вы его готовить не можете.

    Мой пример для задачи (накидал быстро)

    file: routes/web.php
    Route::get('products', [
        'as'   => 'products.show',
        'uses' => 'ProductController@products',
    ]);
    
    Route::get('product/{product}/', [
        'as'   => 'product.show',
        'uses' => 'ProductController@product',
    ])->where('product', '[0-9]*');
    
    Route::get('cart', [
        'as'   => 'cart.show',
        'uses' => 'CartController@show',
    ]);
    
    Route::post('cart/{product}/{action}', [
        'as'   => 'cart.change',
        'uses' => 'CartController@change',
    ])->where([
        'product' => '[0-9]*',
        'action'  => 'append|reduce|clear',
    ]);


    file: app/Http/Controllers/ProductController.php
    <?php namespace App\Http\Controllers;
    
    use App\Product;
    use App\Cart;
    
    class ProductController extends Controller
    {
        const ITEMS_ON_PAGE = 10;
    
        public function products()
        {
            $products = Product::orderBy('price', 'desc')
                ->paginate(self::ITEMS_ON_PAGE);
    
            if (!count($products)) {
                // Как вариант, решать в отображении что сообщить, 
                // так как может не быть товаров вообще
                abort(404);
            }
    
            return view('product.list')
                ->with('products', $products)
                ->with('cart', Cart::summary());
        }
    
        public function product($productId)
        {
            $product = Product::find($productId);
    
            if (!$product) {
                abort(404);
            }
    
            return view('product.item')
                ->with('product', $product)
                ->with('cart', Cart::summary());
        }
    }


    file: app/Http/Controllers/CartController.php
    <?php namespace App\Http\Controllers;
    
    use App\Cart;
    use Illuminate\Support\Facades\Log;
    
    class CartController extends Controller
    {
        public function show()
        {
            return view('cart.show')
                ->with('cart', Cart::all());
        }
    
        public function change($productId, $action)
        {
            try {
                // Изменение корзины внутреннее дело самой корзины
                Cart::change($productId, $action);
            } catch(\Exception $e) {
                // Здесь можно разрешить как не стандартные ситуации, так и стандартные.
                // Но у нас пока одно.
                Log::error('Cart change: ' . $e->getMessage(), [$productId, $action]);
    
                return back()
                    ->with('message', 'Простите, дяденька, засранца.');
            }
    
            // Возвращаемся туда от куда добавился товар или изменилось его количество.
            // А именно страница товара, списка товаров или корзина.
            return back();
        }
    }

    Осталось реализовать модели Cart и Product.
    Кстати, с Product можно по методам и так из коробки работать.
    И конечно можно изменить корзину методом полной очистки и ручного изменения количества. Много кода в laravel это не потребует. И намного прозрачней, чем с кодом Bitrix и Magento работать. Быстро получился прототип своей корзины без бремени универсальности.
  • Как правильно спроектировать Laravel приложение с уклоном в enterprise?

    > Читайте вопрос автора.

    Так и определитесь, возможно использование ActiveRecord в экшенах.

    На самом деле это не обязательно в Laravel. То что так делают - не обязательно означает, что будет использовать топикстартер (хотя для этапа создания прототипа вполне нормально). Впрочем подобное позволяет вообще любой web фреймвок. Есть другая информация?

    > Поверх чего? Поверх ActiveRecord? Зачем он тогда нужен во фреймворке, если есть DataMapping?


    Бизнес - это больше чем заполнение полей данными. Если вы не заметили, то топикстартер упомянул DDD.

    > Значит Larvel не написан на основе Symfony. А написан с использованием компонентов Symfony. Но, суть Фреймворка именно в предложении концепции программисту как строить своё приложение.


    Именно. Отдельные компоненты из Symfony занимаются только тем, что хорошо умеют делать. Зачем писать велосипед. А вот фреймвок Laravel сверху их организует и создаёт флов разработки, который гораздо сложнее в чистом Symfony и совсем магичен в Yii.

    Так что решать проблему целостности данных, вы будете частично в приложении. И так делают 99% разработчиков.

    Но не интерпрайз. А теперь и биг дата, понимаешь.

    Предварительная валидация данных - это не целостность данных. Хотя решается вполне элегантно в Laravel. И кастомные реквесты с валидаторами и мутаторы/акцессоры для модели.

    Если БД построена не для интерпрайза, то хоть Java с его фреймвоками юзай. Всё одно толку не будет.

    Эра быстрых прототипов закончилась, где то в 2012. Сейчас уже нужны комплексные решения.

    Это спорно.
  • Как правильно спроектировать Laravel приложение с уклоном в enterprise?

    Вообще-то работа с ActiveRecord в экшене возможна в простых приложениях, но не обязательна. Для сложной бизнес логики создаются слои поверх.
    В Laravel используются отдельные компоненты Symfony, а не вся его помойка. Используемые компоненты обновляются до последних версий.

    И ещё стоит сказать о магии. Вот поклонники Symfony - это часто поклонники своей магии. Они любят создавать для себя круглые дистрибутивы в вакууме. С чистого Symfony приложения не делаются, так ведь? Впрочем это возможно не к вам. Хотя я не знаю ваше отношение к магии.

    Вопрос же целостности данных решать на уровне PHP не имеет смыла. Это должно решаться на уровне СУБД.

    В PHP нужно решать вопрос быстрой разработки прототипа, вокруг развивающейся структуры БД.
  • Как правильно спроектировать Laravel приложение с уклоном в enterprise?

    Вообще-то Laravel построен на Symfony. Значит и Symfony не стоит использовать?
    Статические вызовы для получения билдера в Eloquent это преступление?
  • Как учить Node.js?

    Порадовал пункт 3.

    Фактически если не нужны сокеты для обмена сообщениями, то nodeJs в пенис не упирался для веб приложений и только создаст проблемы в разработке.

    Правда ещё упустил пункты не упомянутые четыре и пять. Ещё приходится иметь дело с nodeJs если реакт/редьюс (как то с другими технологиями больше магии) и помойка ангуляр - это четыре. Фронтэндеры посаженые на nodeJs - пять.

    Я не о том, что интеграция разработки с php сложная, что вообще интеграция сложная и опять магия и куча мегопайтов сверху.