Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Laravel Tips & Tricks

Laravel Tips & Tricks

How to improve your application using the SOLID principles.

Avatar for Gilberto  Bueno

Gilberto Bueno

February 09, 2018

More Decks by Gilberto Bueno

Other Decks in Programming

Transcript

  1. Via Laravel Installer Via Composer Create-Project composer create-project --prefer-dist laravel/laravel

    my-application composer global require “laravel/installer” . . . laravel new my-application — OR —
  2. IMPROVED DIRECTORY STRUCTURE MY-APPLICATION app Console Exceptions Http Controllers Middleware

    Providers User.php bootstrap config Models UserRepository.php Repositories UserService.php Services
  3. protected function saveCustomer(Klarna_Checkout_Order $order) { . . . $billingAddress =

    $this->customerAddressRepository->findWhere([ 'customer_id' => $customer->id, 'type' => 'billing' ])->first(); if (is_null($billingAddress)) { $this->customerAddressRepository->create([ 'type' => 'billing', 'address' => $order['billing_address']['street_address'], 'zipcode' => $order['billing_address']['postal_code'], 'city' => $order['billing_address']['city'], 'country' => $order['billing_address']['country'], 'customer_id' => $customer->id ]); } else { $this->customerAddressRepository->update([ 'type' => 'billing', 'address' => $order['billing_address']['street_address'], 'zipcode' => $order['billing_address']['postal_code'], 'city' => $order['billing_address']['city'], 'country' => $order['billing_address']['country'], 'customer_id' => $customer->id ], $billingAddress->id); } $shippingAddress = $this->customerAddressRepository->findWhere([ 'customer_id' => $customer->id, 'type' => 'shipping' ])->first(); if (is_null($shippingAddress)) { $this->customerAddressRepository->create([ 'type' => 'shipping', 'address' => $order['shipping_address']['street_address'], 'zipcode' => $order['shipping_address']['postal_code'], 'city' => $order['shipping_address']['city'], 'country' => $order['shipping_address']['country'], 'customer_id' => $customer->id ]); } else { $this->customerAddressRepository->update([ 'type' => 'shipping', 'address' => $order['shipping_address']['street_address'], 'zipcode' => $order['shipping_address']['postal_code'], 'city' => $order['shipping_address']['city'], 'country' => $order['shipping_address']['country'], 'customer_id' => $customer->id ], $billingAddress->id); } . . . } 49 loc 71 loc
  4. S.O.L.I.D. is a mnemonic acronym introduced by Michael Feathers for

    the "first five principles" named by Robert C. Martin in the early 2000s that stands for five basic principles of object-oriented programming and design. https://goo.gl/9EiCHX
  5. DISADVANTAGES
 IN THE BEGINNING 1. Hard to understand 2. You

    will spend more time to conclude a feature 3. Learn more about OOP ¯\_(ツ)_/¯
  6. <?php namespace App\Services; use \Illuminate\Validation\Validator; class UserService { public function

    create($data) { // Validation $validator = Validator::make($data, [ 'name' => 'required', 'email' => 'required|email' ]); if ($validator->fails()) { // ... } // Save user $user = new \App\Models\User(); $user->fill($data); $user->save(); // Send notification to admin mail('[email protected]', 'New user', "Name: {$data['name']}");
 // ... } }
  7. <?php namespace App\Services; use \Illuminate\Validation\Validator; class UserService { public function

    create($data) { // Validation $validator = Validator::make($data, [ 'name' => 'required', 'email' => 'required|email' ]); if ($validator->fails()) { // ... } // Save user $user = new \App\Models\User(); $user->fill($data); $user->save(); // Send notification to admin mail('[email protected]', 'New user', "Name: {$data['name']}");
 // ... } } VIOLATION
  8. <?php namespace App\Services; use \Illuminate\Validation\Validator; class UserService { public function

    create($data) { // Validation $validator = Validator::make($data, [ 'name' => 'required', 'email' => 'required|email' ]); if ($validator->fails()) { // ... } // Save user $user = new \App\Models\User(); $user->fill($data); $user->save(); // Send notification to admin mail('[email protected]', 'New user', "Name: {$data['name']}");
 // ... } }
  9. <?php namespace App\Services; use \Illuminate\Validation\Validator; class UserService { public function

    create($data) { // Validation $validator = Validator::make($data, [ 'name' => 'required', 'email' => 'required|email' ]); if ($validator->fails()) { // ... } // Save user $user = new \App\Models\User(); $user->fill($data); $user->save(); // Send notification to admin mail('[email protected]', 'New user', "Name: {$data['name']}");
 // ... } }
  10. <?php namespace App\Services; use \Illuminate\Validation\Validator; class UserService { public function

    create($data) { // Validation $validator = Validator::make($data, [ 'name' => 'required', 'email' => 'required|email' ]); if ($validator->fails()) { // ... } // Save user $user = new \App\Models\User(); $user->fill($data); $user->save(); // Send notification to admin mail('[email protected]', 'New user', "Name: {$data['name']}");
 // ... } }
  11. <?php namespace App\Services; use \Illuminate\Validation\Validator; class UserService { public function

    create($data) { // Validation $validator = Validator::make($data, [ 'name' => 'required', 'email' => 'required|email' ]); if ($validator->fails()) { // ... } // Save user $user = new \App\Models\User(); $user->fill($data); $user->save(); // Send notification to admin mail('[email protected]', 'New user', "Name: {$data['name']}");
 // ... } }
  12. <?php namespace App\Services; use Illuminate\Validation\Factory as IlluminateValidator; use Illuminate\Support\Facades\Mail; use

    App\Validators\UserValidator as Validator; use App\Mail\UserRegistration; class UserService { /** * @var IlluminateValidator */ protected $validator; public function __construct(Validator $validator) { $this->validator = $validator; } public function create($data) { $this->validator->validate($data); $user = new \App\Models\User(); $user->fill($data); $user->save(); Mail::to('[email protected]')->send(new UserRegistration($user)); } }
  13. <?php namespace App\Services; use Illuminate\Validation\Factory as IlluminateValidator; use Illuminate\Support\Facades\Mail; use

    App\Validators\UserValidator as Validator; use App\Mail\UserRegistration; class UserService { /** * @var IlluminateValidator */ protected $validator; public function __construct(Validator $validator) { $this->validator = $validator; } public function create($data) { $this->validator->validate($data); $user = new \App\Models\User(); $user->fill($data); $user->save(); Mail::to('[email protected]')->send(new UserRegistration($user)); } }
  14. <?php namespace App\Services; use Illuminate\Validation\Factory as IlluminateValidator; use Illuminate\Support\Facades\Mail; use

    App\Validators\UserValidator as Validator; use App\Mail\UserRegistration; class UserService { /** * @var IlluminateValidator */ protected $validator; public function __construct(Validator $validator) { $this->validator = $validator; } public function create($data) { $this->validator->validate($data); $user = new \App\Models\User(); $user->fill($data); $user->save(); Mail::to('[email protected]')->send(new UserRegistration($user)); } }
  15. <?php namespace App\Services; use Illuminate\Validation\Factory as IlluminateValidator; use Illuminate\Support\Facades\Mail; use

    App\Validators\UserValidator as Validator; use App\Mail\UserRegistration; class UserService { /** * @var IlluminateValidator */ protected $validator; public function __construct(Validator $validator) { $this->validator = $validator; } public function create($data) { $this->validator->validate($data); $user = new \App\Models\User(); $user->fill($data); $user->save(); Mail::to('[email protected]')->send(new UserRegistration($user)); } }
  16. <?php namespace App\Validators; /** * Class UserValidator * @package App\Validators

    */ class UserValidator extends Validator { public $rules = [ 'name' => 'required', 'email' => 'required|email' ]; }
  17. <?php namespace App\Mail; use App\Models\User; use Illuminate\Bus\Queueable; use Illuminate\Mail\Mailable; use

    Illuminate\Queue\SerializesModels; class UserRegistration extends Mailable { use Queueable, SerializesModels; /** * @var User */ public $user; public function __construct(User $user) { $this->user = $user; } public function build() { return $this->view('emails.user.registration') ->with([ 'name' => $this->user->name, 'email' => $this->user->email, ]); } }
  18. <?php namespace App\Services; /** * Class CheckoutService * @package App\Services

    */ class CheckoutService { public function confirmation($data, $gateway) { if ($gateway == 'paypal') { // PayPal payment process } if ($gateway == 'pagseguro') { // PagSeguro payment process } } }
  19. <?php namespace App\Services; /** * Class CheckoutService * @package App\Services

    */ class CheckoutService { public function confirmation($data, $gateway) { if ($gateway == 'paypal') { // PayPal payment process } if ($gateway == 'pagseguro') { // PagSeguro payment process } } } VIOLATION
  20. <?php namespace App\Services; /** * Class CheckoutService * @package App\Services

    */ class CheckoutService { public function confirmation($data, $gateway) { if ($gateway == 'paypal') { // PayPal payment process } if ($gateway == 'pagseguro') { // PagSeguro payment process } } }
  21. <?php namespace App\Services; /** * Class CheckoutService * @package App\Services

    */ class CheckoutService { public function confirmation($data, $gateway) { if ($gateway == 'paypal') { // PayPal payment process } if ($gateway == 'pagseguro') { // PagSeguro payment process } } }
  22. <?php namespace App\Services; /** * Class CheckoutService * @package App\Services

    */ class CheckoutService { public function confirmation($data, $gateway) { if ($gateway == 'paypal') { // PayPal payment process } if ($gateway == 'pagseguro') { // PagSeguro payment process } if ($gateway == 'picpay') { // PicPay payment process } } }
  23. <?php namespace App\Services; /** * Class CheckoutService * @package App\Services

    */ class CheckoutService { public function confirmation($data, PaymentGateway $gateway) { ... $gateway->send($data); ... } }
  24. <?php namespace App\Services; /** * Class CheckoutService * @package App\Services

    */ class CheckoutService { public function confirmation($data, PaymentGateway $gateway) { ... $gateway->send($data); ... } }
  25. <?php namespace App\Services; /** * Class CheckoutService * @package App\Services

    */ class CheckoutService { public function confirmation($data, PaymentGateway $gateway) { ... $gateway->send($data); ... } }
  26. <?php namespace App\Services; /** * Class PaymentType * @package App\Services

    */ abstract class PaymentGateway { abstract public function send($data); }
  27. CONCEPT "objects in a program should be replaceable with instances

    of their subtypes without altering the correctness of that program"
  28. <?php namespace App\Services; /** * Class CheckoutService * @package App\Services

    */ class CheckoutService { public function confirmation($data, PaymentGateway $gateway) { ... $gateway->send($data); ... } }
  29. $gateway = new \App\Services\PayPal(); // $gateway = new \App\Services\PicPay(); //

    $gateway = new \App\Services\PagSeguro(); // $gateway = new \App\Services\Cielo(); ... $checkout = new \App\Services\CheckoutService(); $checkout->confirmation($data, $gateway);
  30. <?php namespace App\Service; /** * Interface RegistrationInterface * @package App\Service

    */ interface RegistrationInterface { public function save(); public function sendConfirmation(); }
  31. <?php namespace App\Service; use App\Mail\AccountConfirmation; use Illuminate\Support\Facades\Mail; /** * Class

    UserService * @package App\Service */ class UserService implements RegistrationInterface { public function save() { ... } public function sendConfirmation($user) { ... Mail::to($user->email)->send(new AccountConfirmation($user)); } }
  32. <?php namespace App\Service; /** * Class ProductService * @package App\Service

    */ class ProductService implements RegistrationInterface { public function save() { ... } public function sendConfirmation($user) { // ¯\_(ツ)_/¯ } }
  33. <?php namespace App\Service; /** * Class ProductService * @package App\Service

    */ class ProductService implements RegistrationInterface { public function save() { ... } public function sendConfirmation($user) { // ¯\_(ツ)_/¯ } } VIOLATION
  34. <?php namespace App\Service; /** * Interface UserRegistrationInterface * @package App\Service

    */ interface UserRegistrationInterface { public function save(); public function sendConfirmation($user); }
  35. <?php namespace App\Service; /** * Interface ProductRegistrationInterface * @package App\Service

    */ interface ProductRegistrationInterface { public function save(); public function updateElasticSeach($product); }
  36. <?php namespace App\Service; use App\Mail\AccountConfirmation; use Illuminate\Support\Facades\Mail; /** * Class

    UserService * @package App\Service */ class UserService implements UserRegistrationInterface { public function save() { ... } public function sendConfirmation($user) { ... } }
  37. <?php namespace App\Service; /** * Class ProductService * @package App\Service

    */ class ProductService implements ProductRegistrationInterface { public function save() { ... } public function updateElasticSeach($product) { ... } }
  38. <?php namespace App\Services; use Illuminate\Validation\Factory as IlluminateValidator; use Illuminate\Support\Facades\Mail; use

    App\Validators\UserValidator as Validator; use App\Mail\UserRegistration; class UserService { /** * @var IlluminateValidator */ protected $validator; public function __construct(Validator $validator) { $this->validator = $validator; } public function create($data) { $this->validator->validate($data); $user = new \App\Models\User(); $user->fill($data); $user->save(); Mail::to('[email protected]')->send(new UserRegistration($user)); } }
  39. <?php namespace App\Services; use Illuminate\Validation\Factory as IlluminateValidator; use Illuminate\Support\Facades\Mail; use

    App\Validators\UserValidator as Validator; use App\Mail\UserRegistration; class UserService { /** * @var IlluminateValidator */ protected $validator; public function __construct(Validator $validator) { $this->validator = $validator; } public function create($data) { $this->validator->validate($data); $user = new \App\Models\User(); $user->fill($data); $user->save(); Mail::to('[email protected]')->send(new UserRegistration($user)); } }
  40. <?php namespace App\Services; use Illuminate\Validation\Factory as IlluminateValidator; use Illuminate\Support\Facades\Mail; use

    App\Validators\UserValidator as Validator; use App\Mail\UserRegistration; class UserService { /** * @var IlluminateValidator */ protected $validator; public function __construct(Validator $validator) { $this->validator = $validator; } public function create($data) { $this->validator->validate($data); $user = new \App\Models\User(); $user->fill($data); $user->save(); Mail::to('[email protected]')->send(new UserRegistration($user)); } } VIOLATION
  41. <?php namespace App\Services; use App\Validators\UserValidator as Validator; use App\Models\User; class

    UserService { protected $validator; protected $model; public function __construct(Validator $validator, User $model) { $this->validator = $validator; $this->model = $model; } public function create($data) { $this->validator->validate($data); $this->model—>fill($data); $this->model->save(); ... } }
  42. <?php namespace App\Providers; use Illuminate\Support\ServiceProvider; class AppServiceProvider extends ServiceProvider {

    ... public function register() { $this->app->bind('App\Service\UserService', function ($app) { $validatorFactory = $app->make('validator'); return new \App\Service\UserService( new \App\Validators\UserValidator($validatorFactory) new \App\Model\User(), ); }); } }
  43. <?php namespace App\Providers; use Illuminate\Support\ServiceProvider; class AppServiceProvider extends ServiceProvider {

    ... public function register() { $this->app->bind('App\Service\UserService', function ($app) { $validatorFactory = $app->make('validator'); return new \App\Service\UserService( new \App\Validators\UserValidator($validatorFactory) new \App\Model\User(), ); }); } }
  44. <?php namespace App\Providers; use Illuminate\Contracts\Events\Dispatcher as DispatcherContract; use Illuminate\Foundation\Support\Providers\EventServiceProvider as

    ServiceProvider; class EventServiceProvider extends ServiceProvider { protected $listen = [ 'Illuminate\Database\Events\QueryExecuted' => [ 'App\Listeners\QueryListener', ], ]; public function boot(DispatcherContract $events) { parent::boot($events); } }
  45. <?php namespace App\Listeners; use Illuminate\Database\Events\QueryExecuted; use Illuminate\Support\Facades\Log; class QueryListener {

    public function __construct() { } public function handle(QueryExecuted $event) { list($sql, $bindings, $time, $connectionObj, $connectionName) = array_values(get_object_vars($event)); $query = vsprintf(str_replace(['%', '?'], ['%%', '%s'], $sql), $bindings); Log::info("{$connectionName} | {$time}ms | {$query}"); } }
  46. <?php namespace App\Providers; use App\Models\Tenant\StockEntry; use App\Observers\StockEntryObserver; use Illuminate\Support\ServiceProvider; class

    AppServiceProvider extends ServiceProvider { public function boot() { StockEntry::observe(StockEntryObserver::class); } public function register() { ... } }
  47. <?php namespace App\Observers; use App\Models\Tenant\StockEntryHistory; use Illuminate\Support\Facades\Session; class StockEntryObserver {

    public function creating($stockEntry) { ... } public function updating($stockEntryObj) { ... if ($stockEntryObj->orderRow) { $orderRow = $stockEntryObj->orderRow->getAttributes(); $orderRowOriginal = $stockEntryObj->orderRow->getOriginal(); $updatedOrderFields = array_diff($orderRow, $orderRowOriginal); } ... if (!empty($data)) { StockEntryHistory::insert($data); } } }