Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Sign up for free
Menu
Search
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Pricing
Search
Sign in
Sign up for free
Finding Laravel Best Practices in 2018
Search
nunulk
September 08, 2018
Technology
1.8k
4
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
Finding Laravel Best Practices in 2018
nunulk
September 08, 2018
More Decks by nunulk
See All by nunulk
Laravelでテストしやすいコードを書く5 / 5 tips of building testable modules in Laravel
nunulk
0
350
Design It! in a nutshell
nunulk
1
310
PHP Getting Faster
nunulk
0
290
How does learning English broaden your world?
nunulk
0
420
Testing on Laravel
nunulk
1
1.6k
Vue.js + TypeScript + Firebase
nunulk
0
970
Nuxt.js in TypeScript
nunulk
1
460
A startup meets Laravel + Vue.js
nunulk
1
2.1k
Unit Testing Laravel
nunulk
0
520
Other Decks in Technology
See All in Technology
あるけみー式LTスライド作成術
alchemy1115
1
170
『止めない』を設計する — 制約の中で、事業の根幹を支える判断
hiroyaterui
0
260
GoにおけるFFIのこれまでとこれから
goccy
5
2.3k
Podは生きているのにGoだけが落ちる:GOGCとGOMEMLIMITで追うInvisible OOM Killの謎
tkc66buzz
1
500
From Vanilla Kubernetes to a Batteries-Included Platform: Developer Experience at 1,300+ Clusters
yosshi_
0
680
Claude Code本って、 読む必要あるの?
oikon48
2
380
TinyGo 開発サイクルを高速化する:Go で作るエミュレータ入門
zozotech
PRO
1
600
Omarchy Quattro の日本語設定周り
simosako
2
140
Adaptive Warehouse を今すぐ導入すべき理由と迷ったときの判断基準
__allllllllez__
0
160
日経電子版を支えていく Kasane Design System/fec_fukuoka
nikkei_engineer_recruiting
0
660
OpenTelemetryのメトリクスをCloudWatchに送ってPromQLで見てみた
ota1022
0
120
Redmine 7.0で私が開発した新機能の狙いと背景
vividtone
1
160
Featured
See All Featured
コードの90%をAIが書く世界で何が待っているのか / What awaits us in a world where 90% of the code is written by AI
rkaga
63
45k
Technical Leadership for Architectural Decision Making
baasie
3
560
SEO Brein meetup: CTRL+C is not how to scale international SEO
lindahogenes
1
2.9k
From Legacy to Launchpad: Building Startup-Ready Communities
dugsong
0
330
Fireside Chat
paigeccino
43
4k
Build The Right Thing And Hit Your Dates
maggiecrowley
39
3.4k
ピンチをチャンスに:未来をつくるプロダクトロードマップ #pmconf2020
aki_iinuma
128
56k
Docker and Python
trallard
47
4.2k
Redefining SEO in the New Era of Traffic Generation
szymonslowik
1
410
B2B Lead Gen: Tactics, Traps & Triumph
marketingsoph
0
240
Joys of Absence: A Defence of Solitary Play
codingconduct
1
500
Designing for Performance
lara
611
70k
Transcript
LaravelͰΣϒΞϓϦέʔ γϣϯΛͭ͘Δͱ͖ͷϕετϓϥ ΫςΟεΛ୳Δ in 2018 Cloud Developer - Laravel #1
nunulk
ຊͷτϐοΫ ࣗݾհ -BSBWFMͷಛʹ͍ͭͯ -BSBWFMͷΞʔΩςΫνϟ֓ཁ -BSBWFMͰΣϒΞϓϦέʔγϣϯΛͭ͘Δͱ͖ͷϕετ ϓϥΫςΟεΛ୳Δ
ຊͷΰʔϧ -BSBWFMͷਓؾͷཧ༝ΛΔ -BSBWFMͷΞʔΩςΫνϟΛΔ -BSBWFMͰΣϒΞϓϦέʔγϣϯΛͭ͘Δͱ͖ͷΑΓ Α͍ઃܭɾ࣮ࢦΛΠϝʔδͰ͖ΔΑ͏ʹͳΔ
ࣗݾհ <?php $I->am('nunulk(͵͵Δ͘)') ->workAs('a freelance programmer') ->tackleComplexityOfSoftware() ->with([ 'Laravel', ‘Vue.js’,
]);
DogHuggy
SCOUTER
Laravelͷಛ
Laravelͷಛ
Laravelͷಛ
Laravelͷಛ l5IF1)1'SBNFXPSL'PS8FC"SUJTBOTz w ΦʔϧΠϯύοέʔδ w ߴ͍ੜ࢈ੑ w ׆ൃͳίϛϡχςΟ
Laravelͷಛ w 3VCZPO3BJMT4JOBUSBʹӨڹΛड͚͍ͯΔ ‣ &MPRVFOU03.ɺΫϩʔδϟϕʔεͷϧʔςΟϯά ɺFUD w ෦Ͱଟ͘ͷ4ZNGPOZίϯϙʔωϯτΛར༻͠ɺ֦ு͠ ͍ͯΔ ‣
3FRVFTU 3FTQPOTF $PNNBOE FUD w ๛ͳϔϧύʔΫϥε ‣ 'BDBEF $PMMFDUJPO 'MVFOU 0QUJPOBM FUD
Laravelͷಛ w 7VFKTΛࠝแ͍ͯ͠Δ ‣ -BSBWFM.JYͰϏϧυઃఆΛ؆ૉԽɺϔϧύʔؔΛఏڙ w ๛ͳΦϑΟγϟϧύοέʔδ ‣ $BTIJFS &OWPZ
1BTTQPSU 4PDJBMJUF FUD w ૉૣ͍ϦϦʔεαΠΫϧ ‣ ͓Αͦʹճ ‣ -54
LaravelͷΞʔΩςΫνϟ֓ཁ
Router Middleware Request Kernel Controller Response Model Event Job View
Resource
Routing Route::get('/', function () { return view('welcome'); }); Auth::routes();
Route::get('/home', 'HomeController@index')->name('home');
Middleware public function handle($request, Closure $next, $guard = null) {
if (Auth::guard($guard)->check()) { return redirect('/home'); } return $next($request); }
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', ]; }
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); }
Event public function create(CreateTaskRequest $request) { $task = new Task();
// (snip) $task->save(); TaskCreated::dispatch($task); return new JsonResponse($task, Response::HTTP_CREATED); }
EventListener public function handle(TaskCreatedEvent $event) { $assignee = $event->task->assignee; if
(is_null($assignee)) { return; } $assignee->notify(new TaskCreated($event->task)); }
LaravelͰΣϒΞϓϦέʔγϣϯ Λͭ͘Δͱ͖ͷϕετϓϥΫςΟεΛ ୳Δ
None
ϕετϓϥΫςΟεͷ୳͠ํ •ϕετϓϥΫςΟεέʔεόΠέʔε •͕ͩɺ͋Δ͍ͯͲύλʔϯԽͰ͖Δ •ࣗʢνʔϜʣͷதͰɺج४ΛఆΊ͓ͯ͘͜ͱ͕େࣄ •ܦݧࣝΛ૿͠ɺΞοϓσʔτ͍ͯ͘͠ •ҙ͚ࣝͱͯ͠ʮϕετʯͰͳ͘ʮϕλʔʯ
ϕετϓϥΫςΟεͷ୳͠ํ 1.Ͱ͖Δ͚ͩϑϨʔϜϫʔΫͷ࡞๏ʹ߹ΘͤΔ 2.Ͱ͖Δ͚ͩมߋ༰қੑ͕ߴ͘ͳΔΑ͏ʹ͢Δ 3.Ͱ͖Δ͚ͩτϦοΩʔʹͳΒͳ͍Α͏ʹ͢Δ
ϕετϓϥΫςΟεͷ୳͠ํ 1.Ͱ͖Δ͚ͩϑϨʔϜϫʔΫͷ࡞๏ʹ߹ΘͤΔ ‣ ެࣜυΩϡϝϯτͷίʔυΛਅࣅΔ ‣ ϑϨʔϜϫʔΫͷઃܭࢥΛཧղ͢Δ ‣ Ͱ͖Δ͚ͩϑϨʔϜϫʔΫͷৼΔ͍Λม͑ͳ͍
ϕετϓϥΫςΟεͷ୳͠ํ 1.Ͱ͖Δ͚ͩϑϨʔϜϫʔΫͷ࡞๏ʹ߹ΘͤΔ 2.Ͱ͖Δ͚ͩมߋ༰қੑ͕ߴ͘ͳΔΑ͏ʹ͢Δ ‣ ਆΫϥεΛͭ͘Βͳ͍ ‣ ґଘؔΛͰ͖Δ͚ͩগͳ͘͢Δ ‣ ςετΛॻ͖͍͢Α͏ʹ͢Δ
ϕετϓϥΫςΟεͷ୳͠ํ 1.Ͱ͖Δ͚ͩϑϨʔϜϫʔΫͷ࡞๏ʹ߹ΘͤΔ 2.Ͱ͖Δ͚ͩมߋ༰қੑ͕ߴ͘ͳΔΑ͏ʹ͢Δ 3.Ͱ͖Δ͚ͩτϦοΩʔʹͳΒͳ͍Α͏ʹ͢Δ ‣ Ͱ͖Δ͚ͩՄಡੑߴ͘ॻ͚ΔΑ͏ʹઃܭ͢Δ ‣ ໎ͬͨΒγϯϓϧͳํΛબ͢Δ ‣ τϦοΩʔͳॲཧӅṭʢہॴԽʣ͢Δ
λεΫཧΞϓϦέʔγϣϯΛྫʹ Ұॹʹߟ͑ͯΈ·͠ΐ͏
λεΫΛੜ͢Δͱ͖ͷόϦσʔγϣϯ
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', ]; }
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', ]; }
Request w όϦσʔγϣϯʹ͓͍ͯɺྻڍܕͷ͕ଥ͔Ͳ͏͔ 3VMFJOΛͬͯɺϞσϧ͔Βऔಘ͢ΔΑ͏ʹ͢Δ
ΤϯςΟςΟΛੜ͢Δͱ͖ͷ $POUSPMMFSͱ3FRVFTUͱͷ࿈ܞ
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); }
Controller (after) public function create(CreateTaskRequest $request) { $task = $request->makeTask();
$task->save(); return response()->json($task, Response::HTTP_CREATED); }
Controller (after) class CreateTaskRequest extends FormRequest { public function makeTask():
Task { return new Task([ ‘created_by' => $this->user()->id + $this->validated() ]); } }
Controller (before) public function show($id) { $task = Task::findOrFail($id); //
snip return $task; }
Controller (after) public function show(Task $task) { // snip return
$task; }
Controller w ΤϯςΟςΟͷϓϩύςΟɺͰ͖Δ͚ͩ·ͱΊͯ 3FRVFTU͔Βऔಘ͠ɺมߋՕॴΛগͳ͘͢ΔΑ͏ʹ͢Δ w ϞσϧόΠϯσΟϯάΛੵۃతʹ͍͖ͬͯ·͠ΐ͏
&WFOU+PC/PUJpDBUJPO ͷ͍͚ͱ໋໊
Event/Job/Notification public function create(CreateTaskRequest $request) { $task = $request->makeTask(); $task->save();
TaskCreated::dispatch($task); return response()->json($task, Response::HTTP_CREATED); }
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)); }
EventListener (after) protected $listen = [ 'App\Events\TaskCreated' => [ 'App\Listeners\Task\UpdateTaskAggregates',
'App\Listeners\Task\SendTaskCreatedNotification', ], ];
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)); } }
Event/Job/Notification w ॲཧ్͕தͰࣦഊ͢Δ͜ͱΛߟྀ͠ɺಉҰϦεφͰෳͷ ॲཧΛͤͣɺϦεφΛ͚Δ w &WFOU$POUSPMMFSʹରԠ͢ΔΑ͏ʹ໋໊͢Δʢݱࡏࢺ ܕͰऴΘΔΑ͏ʹʣ ྫʣ5BTL$SFBUF$POUSPMMFS5BTL$SFBUFE w +PCৼΔ͍͕ࣝผ͢ΔΑ͏ʹ໋໊͢Δʢಈࢺ͔Β࢝Ί
Δʣ ྫʣ6QEBUF5BTL"HHSFHBUFT w /PUJpDBUJPO&WFOUͱಉ໋໊͡
&MPRVFOUͰΫΤϦΛΈཱͯΔ
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(); }
Model (after) class SearchTaskQueryBuilder { private $params; public function
__construct(array $params) { $this->params = $params; } public function build(Builder $builder): Builder { // ͜͜ͰΫΤϦΛϏϧυ͢Δ } }
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(); }
Model w υϝΠϯͰҙຯΛ࣋ͭΫΤϦͷ࣮ΫΤϦείʔϓϝ ιουʹด͡ࠐΊΔ w ΫΤϦͷߏங͕ෳࡶʹͳͬͨΒɺଞͷΫϥεʹͤΔ͜ ͱͰ͖Δ
ΞΫηαͱϛϡʔςʔλΛ͏
Model (before) $ php artisan tinker Psy Shell v0.9.7 (PHP
7.1.16 — cli) by Justin Hileman >>> dump($task->priority) => "normal"
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" }
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(); }
Model w ΞΫηαͱϛϡʔςʔλΛͬͯϓϩύςΟΛϓϦϛςΟ ϒܕ͔ΒυϝΠϯͷܕॻ͖͑Δ͜ͱ͕Ͱ͖Δ w ΞΫηαΛར༻ͯ͠ɺࢉग़ϓϩύςΟΛѻ͏͜ͱͰ͖Δ
%*αʔϏείϯςφ
DI (before) class ExternalItsController extends Controller { private $issueTracker;
public function __construct() { $this->issueTracker = new GitHub(); } public function index() { // snip } }
DI (after) class ExternalItsController extends Controller { private $issueTracker;
public function __construct(IssueTracker $issueTracker) { $this->issueTracker = $issueTracker; } public function index() { $issueTracker = app(IssueTracker::class); // snip } }
DI (after) class AppServiceProvider extends ServiceProvider { public function register()
{ $this->app->bind(IssueTracker::class, function () { return new GitHub(); }); } }
DI (after) class AppServiceProvider extends ServiceProvider { public function register()
{ $this->app ->when(ExternalItsController::class) ->needs(IssueTracker::class) ->give(function () { return new GitHub(); }); } }
ͦͷଞ
ͦͷଞ w 1)16OJUΛ֦ுͯ͠ɺςετͷ͕͢͠͞Ξοϓ͍ͯ͠Δ w γϯϓϧͳ͕ΒπϘΛԡ͑ͨ͞#MBEFςϯϓϨʔτΤϯ δϯ w "SSɺ4USɺ$PMMFDUJPOͳͲͷศརͳϔϧύʔΫϥεؔ ͳͲͳͲ ͝ڵຯ͋ΔํௐͯΈ͍ͯͩ͘͞
·ͱΊ
·ͱΊ w ·ͣ-BSBWFMͰͳʹ͕Ͱ͖Δͷ͔ΛΔ w ෳͷબࢶͷத͔Βɺzϕλʔzͱஅͨ͠ͷΛબͿ w ʑࣝܦݧΛͱʹΞοϓσʔτ͍ͯ͘͠