Talk at Laracon US
Freek Van der Herten

Slides: https://speakerdeck.com/freekmurze/talk-at-laracon-us

Spatie, @freekmurze, freek.dev, ohdear.app

Getting right into it!

Tips and tricks that we've picked up.

Starting with simple Laravel application...create a controller. Instead of closure, using welcome controller.

class WelcomeController
{
  public function __invoke()
    {
        return view('welcome');
    }
}

Might not need to extend Controller class, since all it does is include three classes! You could just use individual traits from the base Controller class as needed.

In route service provider, namespace of controller gets automatically added. Remove it, then in routes, you can just use the class name and ::class, now renaming/refactoring it will also update it in the routes file!

class WelcomeController
{
    public function __invoke()
    {
        return redirect()->action([WelcomeLaraconCOntroller::class, 'anotherAction']);
    }

    public function anotherAction()
    {
        return view('welcome');
    }
}

Removing default namespace, refactoring/changing controller names becomes less brittle!

Extract actions

Special actions, put into their own controllers (like for publishing a post). Move action its own controller, change action name to __invoke, all good

Procedure is in controller itself, but imagine you want command that also does that. Copy it into command? No! Instead, create Actions folder, PublishPostAction class with execute(Post $post).

Controller's invoke method should have the action, then call the execute method on it. Can also include the action in a publish post command. Inject publish-post action in constructor, can do it easily!

In action, can still inject other classes (e.g. TwitterApi) in constructor. That way, you can get code completion and you don't have to go through the app resolver.

Execute the action, assert that it was fulfilled.

View Models

Title, text...let's take a look at PostController. Taking a look at the create and edit functions, they both use categories. Do you want to add them both separately? No!

They add a layer between controller and view, called View Model.

Package is laravel-viewmodels (?)

Post model. Initialize the fields, adding categories in __constructor of PostViewModel

// either
return view('posts.create', new PostViewModel());

// or
return view('posts.edit', new PostViewModel($post));

Can also use function in view model named properly to avoid defining public property

Can also move "is the current category the selected one?" by creating isSelected category in the view model, then replace inline check with @if($isSelected($category))

Can also return a string and just do {{ $isSelected($category) }} which would return the 'selected' or '' string

Ten minutes left for two subjects...

Blade-X

Same application with post controller...

<input-field name="title" :model="$post" />

Like in Vue, you can also pass as an object

In AppServiceProvider@register, BladeX::component('components.*');, then creating views/components/inputField.blade.php

$name for key, $model->$name for value

Can also create <context :model="$post"></context> to avoid passing :model="$post" in each thing

In category select and field label, code for labelling

Can also add into view model! <category-select name="category_id" />, can be re-used anywhere

Query Builder

Example: filter[title]=atinclude=commentssort=-title

In API Posts controller, $query = Post::query() and a bunch of if $request->has('thing')

Using their query builder thing though...inject PostQuery and return $query->paginate()

$this
    ->allowedFilters('title', 'text')
    ->allowedSorts('title')
    ->allowedInclude('comments');

Simplifies a 50-line method into 1

Packages