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

Finding Laravel Best Practices in 2018

73b0a25ed03f649ec63ded82ce4ac7cb?s=47 nunulk
September 08, 2018

Finding Laravel Best Practices in 2018

73b0a25ed03f649ec63ded82ce4ac7cb?s=128

nunulk

September 08, 2018
Tweet

Transcript

  1. LaravelͰ΢ΣϒΞϓϦέʔ γϣϯΛͭ͘Δͱ͖ͷϕετϓϥ ΫςΟεΛ୳Δ in 2018 Cloud Developer - Laravel #1


    nunulk
  2. ຊ೔ͷτϐοΫ  ࣗݾ঺հ  -BSBWFMͷಛ௕ʹ͍ͭͯ  -BSBWFMͷΞʔΩςΫνϟ֓ཁ  -BSBWFMͰ΢ΣϒΞϓϦέʔγϣϯΛͭ͘Δͱ͖ͷϕετ ϓϥΫςΟεΛ୳Δ

  3. ຊ೔ͷΰʔϧ  -BSBWFMͷਓؾͷཧ༝Λ஌Δ  -BSBWFMͷΞʔΩςΫνϟΛ஌Δ  -BSBWFMͰ΢ΣϒΞϓϦέʔγϣϯΛͭ͘Δͱ͖ͷΑΓ Α͍ઃܭɾ࣮૷ࢦ਑ΛΠϝʔδͰ͖ΔΑ͏ʹͳΔ

  4. ࣗݾ঺հ <?php $I->am('nunulk(͵͵Δ͘)')
 ->workAs('a freelance programmer') ->tackleComplexityOfSoftware() ->with([ 'Laravel',
 ‘Vue.js’,

    ]);
  5. DogHuggy

  6. SCOUTER

  7. Laravelͷಛ௕

  8. Laravelͷಛ௕

  9. Laravelͷಛ௕

  10. Laravelͷಛ௕ l5IF1)1'SBNFXPSL'PS8FC"SUJTBOTz w ΦʔϧΠϯύοέʔδ w ߴ͍ੜ࢈ੑ w ׆ൃͳίϛϡχςΟ

  11. Laravelͷಛ௕ w 3VCZPO3BJMT4JOBUSBʹӨڹΛड͚͍ͯΔ ‣ &MPRVFOU03.ɺΫϩʔδϟϕʔεͷϧʔςΟϯά ɺFUD w ಺෦Ͱଟ͘ͷ4ZNGPOZίϯϙʔωϯτΛར༻͠ɺ֦ு͠ ͍ͯΔ ‣

    3FRVFTU 3FTQPOTF $PNNBOE FUD w ๛෋ͳϔϧύʔΫϥε ‣ 'BDBEF $PMMFDUJPO 'MVFOU 0QUJPOBM FUD
  12. Laravelͷಛ௕ w 7VFKTΛࠝแ͍ͯ͠Δ ‣ -BSBWFM.JYͰϏϧυઃఆΛ؆ૉԽɺϔϧύʔؔ਺Λఏڙ w ๛෋ͳΦϑΟγϟϧύοέʔδ ‣ $BTIJFS &OWPZ

    1BTTQPSU 4PDJBMJUF FUD w ૉૣ͍ϦϦʔεαΠΫϧ ‣ ͓Αͦ൒೥ʹճ ‣ -54͸೥
  13. LaravelͷΞʔΩςΫνϟ֓ཁ

  14. Router Middleware Request Kernel Controller Response Model Event Job View

    Resource
  15. Routing 
 Route::get('/', function () { return view('welcome'); }); Auth::routes();

    Route::get('/home', 'HomeController@index')->name('home');
  16. Middleware public function handle($request, Closure $next, $guard = null) {

    if (Auth::guard($guard)->check()) { return redirect('/home'); } return $next($request); }
  17. Request public function authorize() { return true; } public function

    rules() { return [ 'subject' => 'required', 'priority' => ‘required|in:high,normal,low’, 'status' => ‘required|in:open,in_progress,close’, 'due_to' => ‘nullable|date', ‘assigned_to' => ‘nullable|exists:users,id’, 'description' => 'nullable|max:800', ]; }
  18. Controller public function create(CreateTaskRequest $request) { $task = new Task();

    $task->subject = $request->input('subject'); $task->assigned_to = $request->input(‘assigned_to'); $task->priority = $request->input('priority'); $task->status = $request->input('status'); $task->due_to = $request->input('due_to'); $task->description = $request->input('description'); $task->created_by = Auth::id(); $task->save();
 return new JsonResponse($task, Response::HTTP_CREATED); }
  19. Event public function create(CreateTaskRequest $request) { $task = new Task();


    // (snip)
 $task->save();
 TaskCreated::dispatch($task);
 return new JsonResponse($task, Response::HTTP_CREATED); }
  20. EventListener public function handle(TaskCreatedEvent $event) { $assignee = $event->task->assignee; if

    (is_null($assignee)) { return; } $assignee->notify(new TaskCreated($event->task)); }
  21. LaravelͰ΢ΣϒΞϓϦέʔγϣϯ Λͭ͘Δͱ͖ͷϕετϓϥΫςΟεΛ ୳Δ

  22. None
  23. ϕετϓϥΫςΟεͷ୳͠ํ •ϕετϓϥΫςΟε͸έʔεόΠέʔε •͕ͩɺ͋Δ͍ͯͲ͸ύλʔϯԽͰ͖Δ •ࣗ෼ʢνʔϜʣͷதͰɺج४ΛఆΊ͓ͯ͘͜ͱ͕େࣄ •ܦݧ΍஌ࣝΛ૿΍͠ɺΞοϓσʔτ͍ͯ͘͠ •ҙࣝ෇͚ͱͯ͠͸ʮϕετʯͰ͸ͳ͘ʮϕλʔʯ

  24. ϕετϓϥΫςΟεͷ୳͠ํ 1.Ͱ͖Δ͚ͩϑϨʔϜϫʔΫͷ࡞๏ʹ߹ΘͤΔ 2.Ͱ͖Δ͚ͩมߋ༰қੑ͕ߴ͘ͳΔΑ͏ʹ͢Δ 3.Ͱ͖Δ͚ͩτϦοΩʔʹͳΒͳ͍Α͏ʹ͢Δ

  25. ϕετϓϥΫςΟεͷ୳͠ํ 1.Ͱ͖Δ͚ͩϑϨʔϜϫʔΫͷ࡞๏ʹ߹ΘͤΔ ‣ ެࣜυΩϡϝϯτͷίʔυΛਅࣅΔ ‣ ϑϨʔϜϫʔΫͷઃܭࢥ૝Λཧղ͢Δ ‣ Ͱ͖Δ͚ͩϑϨʔϜϫʔΫͷৼΔ෣͍Λม͑ͳ͍

  26. ϕετϓϥΫςΟεͷ୳͠ํ 1.Ͱ͖Δ͚ͩϑϨʔϜϫʔΫͷ࡞๏ʹ߹ΘͤΔ 2.Ͱ͖Δ͚ͩมߋ༰қੑ͕ߴ͘ͳΔΑ͏ʹ͢Δ ‣ ਆΫϥεΛͭ͘Βͳ͍ ‣ ґଘؔ܎ΛͰ͖Δ͚ͩগͳ͘͢Δ ‣ ςετΛॻ͖΍͍͢Α͏ʹ͢Δ

  27. ϕετϓϥΫςΟεͷ୳͠ํ 1.Ͱ͖Δ͚ͩϑϨʔϜϫʔΫͷ࡞๏ʹ߹ΘͤΔ 2.Ͱ͖Δ͚ͩมߋ༰қੑ͕ߴ͘ͳΔΑ͏ʹ͢Δ 3.Ͱ͖Δ͚ͩτϦοΩʔʹͳΒͳ͍Α͏ʹ͢Δ ‣ Ͱ͖Δ͚ͩՄಡੑߴ͘ॻ͚ΔΑ͏ʹઃܭ͢Δ ‣ ໎ͬͨΒγϯϓϧͳํΛબ୒͢Δ ‣ τϦοΩʔͳॲཧ͸ӅṭʢہॴԽʣ͢Δ

  28. λεΫ؅ཧΞϓϦέʔγϣϯΛྫʹ Ұॹʹߟ͑ͯΈ·͠ΐ͏

  29. λεΫΛੜ੒͢Δͱ͖ͷόϦσʔγϣϯ

  30. Request (before) public function authorize() { return true; } public

    function rules() { return [ 'subject' => 'required', 'priority' => ‘required|in:high,normal,low’, 'status' => ‘required|in:open,in_progress,close’, 'due_to' => ‘nullable|date', ‘assigned_to' => ‘nullable|exists:users,id’, 'description' => 'nullable|max:800', ]; }
  31. Request (after) // CreateTaskRequest public function authorize() { return true;

    } public function rules() { return [ ‘subject' => ‘required|max:255’, 'priority' => ['required', Rule::in(Priority::values())], 'status' => ['required', Rule::in(Status::values())], 'due_to' => ‘nullable|date’, ‘assigned_to' => ‘nullable|exists:users,id’, 'description' => ‘nullable|max:800', ]; }
  32. Request w όϦσʔγϣϯʹ͓͍ͯɺྻڍܕͷ஋͕ଥ౰͔Ͳ͏͔͸ 3VMFJOΛ࢖ͬͯɺϞσϧ͔Βऔಘ͢ΔΑ͏ʹ͢Δ

  33. ΤϯςΟςΟΛੜ੒͢Δͱ͖ͷ $POUSPMMFSͱ3FRVFTUͱͷ࿈ܞ

  34. Controller (before) public function create(CreateTaskRequest $request) { $task = new

    Task(); $task->subject = $request->input('subject'); $task->assigned_to = $request->input(‘assigned_to'); $task->priority = $request->input('priority'); $task->status = $request->input('status'); $task->due_to = $request->input('due_to'); $task->description = $request->input('description'); $task->created_by = Auth::id(); $task->save();
 return new JsonResponse($task, Response::HTTP_CREATED); }
  35. Controller (after) public function create(CreateTaskRequest $request) { $task = $request->makeTask();

    $task->save();
 return response()->json($task, Response::HTTP_CREATED); }
  36. Controller (after) class CreateTaskRequest extends FormRequest { public function makeTask():

    Task { return new Task([
 ‘created_by' => $this->user()->id
 + $this->validated()
 ]); } }
  37. Controller (before) public function show($id) { $task = Task::findOrFail($id); //

    snip return $task; }
  38. Controller (after) public function show(Task $task) {
 // snip return

    $task; }
  39. Controller w ΤϯςΟςΟͷϓϩύςΟ͸ɺͰ͖Δ͚ͩ·ͱΊͯ 3FRVFTU͔Βऔಘ͠ɺมߋՕॴΛগͳ͘͢ΔΑ͏ʹ͢Δ w ϞσϧόΠϯσΟϯάΛੵۃతʹ࢖͍͖ͬͯ·͠ΐ͏

  40. &WFOU+PC/PUJpDBUJPO
 ͷ࢖͍෼͚ͱ໋໊

  41. Event/Job/Notification public function create(CreateTaskRequest $request) { $task = $request->makeTask();
 $task->save();


    TaskCreated::dispatch($task);
 return response()->json($task, Response::HTTP_CREATED); }
  42. EventListener (before) public function handle(TaskCreatedEvent $event) { UpdateTaskAggregates::dispatch($task);
 $assignee =

    $event->task->assignee; if (is_null($assignee)) { return; } $assignee->notify(new TaskCreated($event->task)); }
  43. EventListener (after) protected $listen = [ 'App\Events\TaskCreated' => [ 'App\Listeners\Task\UpdateTaskAggregates',

    'App\Listeners\Task\SendTaskCreatedNotification', ], ];
  44. EventListener (after) class UpdateTaskAggregates implements ShouldQueue { public function handle(TaskCreatedEvent

    $event) { UpdateTaskAggregatesJob::dispatch($task);
 } }
 
 class SendTaskCreatedNotification implements ShouldQueue { public function handle(TaskCreatedEvent $event) { $assignee = $event->task->assignee; if (is_null($assignee)) { return; } $assignee->notify(new TaskCreated($event->task)); } }
  45. Event/Job/Notification w ॲཧ్͕தͰࣦഊ͢Δ͜ͱΛߟྀ͠ɺಉҰϦεφͰෳ਺ͷ ॲཧΛͤͣɺϦεφΛ෼͚Δ w &WFOU͸$POUSPMMFSʹରԠ͢ΔΑ͏ʹ໋໊͢Δʢݱࡏ෼ࢺ ܕͰऴΘΔΑ͏ʹʣ
 ྫʣ5BTL$SFBUF$POUSPMMFS5BTL$SFBUFE w +PC͸ৼΔ෣͍͕ࣝผ͢ΔΑ͏ʹ໋໊͢Δʢಈࢺ͔Β࢝Ί

    Δʣ
 ྫʣ6QEBUF5BTL"HHSFHBUFT w /PUJpDBUJPO͸&WFOUͱಉ໋໊͡
  46. &MPRVFOUͰΫΤϦΛ૊ΈཱͯΔ

  47. Model (before) 
 public function search(SearchTaskRequest $request) { $query =

    Task::latest(); $priority = $request->input('priority'); if ($priority) { $query->where('priority', $priority); } $dueToFrom = $request->input(‘due_to.from'); if ($dueToFrom) { $query->where('due_to', ‘>=‘, $dueToFrom); } $dueToTo = $request->input(‘due_to.to’); if ($dueToTo) { $query->where('due_to', ‘<=‘, $dueToTo); } return $query->get(); }
  48. Model (after) 
 class SearchTaskQueryBuilder { private $params; public function

    __construct(array $params) { $this->params = $params; } public function build(Builder $builder): Builder { // ͜͜ͰΫΤϦΛϏϧυ͢Δ
 } }

  49. Model (after) 
 // Task public function scopeSearch(
 Builder $query,

    SearchTaskQueryBuilder $builder) { return $builder->build($query); }
 
 // Task\SearchTaskController
 public function __invoke(SearchTaskRequest $request) { return Task::search($request->createQueryBuilder())->get(); }
  50. Model w υϝΠϯ಺ͰҙຯΛ࣋ͭΫΤϦͷ࣮૷͸ΫΤϦείʔϓϝ ιουʹด͡ࠐΊΔ w ΫΤϦͷߏங͕ෳࡶʹͳͬͨΒɺଞͷΫϥεʹ೚ͤΔ͜ ͱ΋Ͱ͖Δ

  51. ΞΫηαͱϛϡʔςʔλΛ࢖͏

  52. Model (before) $ php artisan tinker Psy Shell v0.9.7 (PHP

    7.1.16 — cli) by Justin Hileman >>> dump($task->priority) => "normal"
  53. Model (after) $ php artisan tinker Psy Shell v0.9.7 (PHP

    7.1.16 — cli) by Justin Hileman >>> dump($task->priority) App\Models\Task\Priority {#2878 #value: "normal" }
  54. Model (after) // Task /** * @param $value * @return

    Priority * @throws \UnexpectedValueException */ public function getPriorityAttribute(string $value): Priority { return new Priority($value); } /** * @param Priority $priority * @return void */ public function setPriorityAttribute(Priority $priority) { $this->attributes['priority'] = $priority->getValue(); }
  55. Model w ΞΫηαͱϛϡʔςʔλΛ࢖ͬͯϓϩύςΟΛϓϦϛςΟ ϒܕ͔ΒυϝΠϯͷܕ΁ॻ͖׵͑Δ͜ͱ͕Ͱ͖Δ w ΞΫηαΛར༻ͯ͠ɺࢉग़ϓϩύςΟΛѻ͏͜ͱ΋Ͱ͖Δ

  56. %*αʔϏείϯςφ

  57. DI (before) 
 class ExternalItsController extends Controller { private $issueTracker;

    public function __construct() { $this->issueTracker = new GitHub(); } public function index() { // snip
 } }

  58. DI (after) 
 class ExternalItsController extends Controller { private $issueTracker;

    public function __construct(IssueTracker $issueTracker) { $this->issueTracker = $issueTracker; } public function index() {
 $issueTracker = app(IssueTracker::class); // snip
 } }

  59. DI (after) class AppServiceProvider extends ServiceProvider { public function register()

    { $this->app->bind(IssueTracker::class, function () { return new GitHub(); }); } }
  60. DI (after) class AppServiceProvider extends ServiceProvider { public function register()

    { $this->app ->when(ExternalItsController::class) ->needs(IssueTracker::class) ->give(function () { return new GitHub(); }); } }
  61. ͦͷଞ

  62. ͦͷଞ w 1)16OJUΛ֦ுͯ͠ɺςετͷ͠΍͕͢͞Ξοϓ͍ͯ͠Δ w γϯϓϧͳ͕Β΋πϘΛԡ͑ͨ͞#MBEFςϯϓϨʔτΤϯ δϯ w "SSɺ4USɺ$PMMFDUJPOͳͲͷศརͳϔϧύʔΫϥε΍ؔ਺ ͳͲͳͲ ͝ڵຯ͋Δํ͸ௐ΂ͯΈ͍ͯͩ͘͞

  63. ·ͱΊ

  64. ·ͱΊ w ·ͣ͸-BSBWFMͰͳʹ͕Ͱ͖Δͷ͔Λ஌Δ w ෳ਺ͷબ୒ࢶͷத͔Βɺzϕλʔzͱ൑அͨ͠΋ͷΛબͿ w ೔ʑ஌ࣝ΍ܦݧΛ΋ͱʹΞοϓσʔτ͍ͯ͘͠