Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
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
300
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
510
Other Decks in Technology
See All in Technology
ガバメントクラウドでのランサムウェア対策
techniczna
1
530
歴史から理解するクラウドインフラのしくみ
kizawa2020
1
200
Pavlokで始める電撃駆動開発
sgrsn
0
140
『モンスターストライク』 の運営に伴走する! データ民主化への 解析グループの3つのアプローチ
mixi_engineers
PRO
0
200
BigQuery を検索ソースとした AI Agent の作り方って 〇〇 通りあんねん
satohjohn
0
150
「待ち時間」の消滅と「自我消耗」の加速:生成AI時代のエンジニアを救うメンタル・リソース管理
poropinai1966
0
400
CloudWatchから始めるAWS監視
butadora
0
310
AIツールを導入しても生産性はあがらない? カオナビが直面した 3つの壁と乗り越え方。/ Overcoming 3 Barriers to AI-Driven Productivity at kaonavi
kaonavi
0
1.4k
初めてのGitHub Actions / GitHub Actions at First
tooppoo
0
120
AI工学特論: MLOps・継続的評価
asei
11
3.1k
AI駆動開発は個人技からチーム戦へ:組織でAIを使いこなすための実践設計
moongift
PRO
0
390
QAタスクをスキル化したいときに考えること
aomoriringo
0
140
Featured
See All Featured
Raft: Consensus for Rubyists
vanstee
141
7.6k
Responsive Adventures: Dirty Tricks From The Dark Corners of Front-End
smashingmag
254
22k
コードの90%をAIが書く世界で何が待っているのか / What awaits us in a world where 90% of the code is written by AI
rkaga
62
45k
The Organizational Zoo: Understanding Human Behavior Agility Through Metaphoric Constructive Conversations (based on the works of Arthur Shelley, Ph.D)
kimpetersen
PRO
0
400
A Guide to Academic Writing Using Generative AI - A Workshop
ks91
PRO
1
360
The untapped power of vector embeddings
frankvandijk
2
1.8k
A Modern Web Designer's Workflow
chriscoyier
698
190k
Claude Code どこまでも/ Claude Code Everywhere
nwiizo
66
57k
Abbi's Birthday
coloredviolet
3
8.9k
Why Your Marketing Sucks and What You Can Do About It - Sophie Logan
marketingsoph
0
340
State of Search Keynote: SEO is Dead Long Live SEO
ryanjones
0
240
sira's awesome portfolio website redesign presentation
elsirapls
0
310
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 ʑࣝܦݧΛͱʹΞοϓσʔτ͍ͯ͘͠