Closed18

Laravelを知る

isopppisoppp

概要系

ローカル開発環境

面倒くさそうなのでValetいれていく
valet link -> valet open
valet unlink

https://laravel.com/docs/8.x/valet

uninstall
https://stackoverflow.com/a/49935975/12981245

こっち?
https://laravel.com/docs/8.x/valet#resetting-your-installation

教材

若干古そうだがこれ 2019 April
Laravel5.2あたりの言及が多いのでベースが古いっぽいものの基礎のお勉強なので大丈夫そう
たまに動かないコードがあったりはする
(追記)読み終わったものの良書だったと思う。6系の言及もあったりで、一応publishの時期で更新はされていそう。最新の話題は勿論ないものの、基礎を学ぶには十分だった

https://learning.oreilly.com/library/view/laravel-up/9781492041207/

後で調べたい

  • signedRoute
  • Custom Route Model Binding
  • view composer
  • when/unless(eloquent)
isopppisoppp

Route/Controller

__invokeで単発function

// \App\Http\Controllers\UpdateUserAvatar.php
public function __invoke(User $user)
{
    // Update the user's avatar image
}

productionでは php artisan route:cache をするべし?

abort

Route::post('something-you-cant-do', function (Illuminate\Http\Request $request) {
    abort(403, 'You cannot do that!');
    abort_unless($request->has('magicToken'), 403);
    abort_if($request->user()->isBanned, 403);
});
isopppisoppp

Commands

php artisan route:list
route一覧

php artisan make:controller XXXXController --api
CRUDのController雛形

php artisan make:controller XXXXController --resource
CRUDとviewの雛形

php artisan migrate
migrate

php artisan migrate:xx --seed
with any command with seeds

php artisan db:seed --class=VotesTableSeeder
add seeds and can run independently

migrate:reset
rollback every database migration

migrate:refresh
rollback every database migration and then run migrate

migrate:fresh
Drop all tabels and run migrate

migrate:status
Shows a table listing every migration

結構ここにまとまっている
https://qiita.com/okdyy75/items/e0ebde94dd0e69df1c45

isopppisoppp

Blade Template

echoのwrapが {{ }}

@がディレクティブ

if

@if (count($talks) === 1)
    There is one talk at this time period.
@elseif (count($talks) === 0)
    There are no talks at this time period.
@else
    There are {{ count($talks) }} talks at this time period.
@endif

unless/endunless

@unless ($user->hasPaid())
    You can complete your payment by switching to the payment tab.
@endunless

loop/@for

@for ($i = 0; $i < $talk->slotsCount(); $i++)
    The number is {{ $i }}<br>
@endfor

loop/@foreach/@endforeach

@foreach ($talks as $talk)
    • {{ $talk->title }} ({{ $talk->length }} minutes)<br>
@endforeach

loop/@while

@while ($item = array_pop($items))
    {{ $item->orSomething() }}<br>
@endwhile

loop/@forelse

@forelse ($talks as $talk)
    • {{ $talk->title }} ({{ $talk->length }} minutes)<br>
@empty
    No talks this day.
@endforelse

Layouts/Partials

<!-- resources/views/layouts/master.blade.php -->
<html>
    <head>
        <title>My Site | @yield('title', 'Home Page')</title>
    </head>
    <body>
        <div class="container">
            @yield('content')
        </div>
        @section('footerScripts')
            <script src="app.js"></script>
        @show
    </body>
</html>
<!-- resources/views/sign-up-button.blade.php -->
<a class="button button--callout" data-page-name="{{ $pageName }}">
    <i class="exclamation-icon"></i> {{ $text }}
</a>
<!-- resources/views/dashboard.blade.php -->
@extends('layouts.master')

@section('title', 'Dashboard')

@section('content')
    Welcome to your application dashboard!
    @include('sign-up-button', ['text' => 'See just how great it is'])
@endsection

@section('footerScripts')
    @parent
    <script src="dashboard.js"></script>
@endsection

conditionally include

{{-- Include a view if it exists --}}
@includeIf('sidebars.admin', ['some' => 'data'])

{{-- Include a view if a passed variable is truth-y --}}
@includeWhen($user->isAdmin(), 'sidebars.admin', ['some' => 'data'])

{{-- Include the first view that exists from a given array of views --}}
@includeFirst(['customs.header', 'header'], ['some' => 'data'])

@each

<!-- resources/views/sidebar.blade.php -->
<div class="sidebar">
    @each('partials.module', $modules, 'module', 'partials.empty-module')
</div>

<!-- resources/views/partials/module.blade.php -->
<div class="sidebar-module">
    <h1>{{ $module->title }}</h1>
</div>

<!-- resources/views/partials/empty-module.blade.php -->
<div class="sidebar-module">
    No modules :(
</div>

@stack/@push/@prepend

<!-- resources/views/layouts/app.blade.php -->
<html>
<head><!-- the head --></head>
<body>
    <!-- the rest of the page -->
    <script src="/css/global.css"></script>
    <!-- the placeholder where stack content will be placed -->
    @stack('scripts')
</body>
</html>

<!-- resources/views/jobs.blade.php -->
@extends('layouts.app')

@push('scripts')
    <!-- push something to the bottom of the stack -->
    <script src="/css/jobs.css"></script>
@endpush

<!-- resources/views/jobs/apply.blade.php -->
@extends('jobs')

@prepend('scripts')
    <!-- push something to the top of the stack -->
    <script src="/css/jobs--apply.css"></script>
@endprepend

$slot / @component / @slot

<!-- resources/views/partials/modal.blade.php -->
<div class="modal">
    <div class="modal-header">{{ $title }}</div>
    <div>{{ $slot }}</div>
    <div class="close button etc">...</div>
</div>
<!-- in another template -->
@component('partials.modal')
   @slot('title')
        Password validation failure
    @endslot

    <p>The password you have provided is not valid.
    Here are the rules for valid passwords: [...]</p>

    <p><a href="#">...</a></p>
@endcomponent

Custom Blade Directive

// Binding
Blade::directive('newlinesToBr', function ($expression) {
    return "<?php echo nl2br({$expression}); ?>";
});

// In use
<p>@newlinesToBr($message->body)</p>
isopppisoppp

Databases and Eloquent ( Migration / Seed / Factory )

create migration
artisan make:migration xxxx
artisan make:migration add_xxxx --table=xxxs
artisan make:migration create_xxxx --create=xxxs

Column Types
https://laravel.com/docs/8.x/migrations#available-column-types

Index

// After columns are created...
$table->primary('primary_id'); // Primary key; unnecessary if used increments()
$table->primary(['first_name', 'last_name']); // Composite keys
$table->unique('email'); // Unique index
$table->unique('email', 'optional_custom_index_name'); // Unique index
$table->index('amount'); // Basic index
$table->index('amount', 'optional_custom_index_name'); // Basic index

Foreign Key

$table->foreign('user_id')
    ->references('id')
    ->on('users')
    ->onDelete('cascade');

create seed
php artisan make:seeder ContactsTableSeeder

use Illuminate\Database\Seeder;
use Illuminate\Database\Eloquent\Model;

class ContactsTableSeeder extends Seeder
{
    public function run()
    {
        DB::table('contacts')->insert([
            'name' => 'Lupita Smith',
            'email' => 'lupita@gmail.com',
        ]);
    }
}

factory
php artisan make:factory ContactFactory

$factory->define(Contact::class, function (Faker\Generator $faker) {
    return [
        'name' => $faker->name,
        'email' => $faker->email,
    ];
});
// Create one
$contact = factory(Contact::class)->create();

// Create many
factory(Contact::class, 20)->create();

in use

$post = factory(Post::class)->create([
    'title' => 'My greatest post ever',
]);

factory(User::class, 20)->create()->each(function ($u) use ($post) {
    $post->comments()->save(factory(Comment::class)->make([
        'user_id' => $u->id,
    ]));
});
$factory->define(Contact::class, function (Faker\Generator $faker) {
    return [
        'name' => 'Lupita Smith',
        'email' => 'lupita@gmail.com',
        'company_id' => function () {
            return factory(App\Company::class)->create()->id;
        },
        'company_size' => function ($contact) {
            // Uses the "company_id" property generated above
            return App\Company::find($contact['company_id'])->size;
        },
    ];
});

multiple factory states

$factory->define(Contact::class, function (Faker\Generator $faker) {
    return [
        'name' => $faker->name,
        'email' => $faker->email,
    ];
});

$factory->state(Contact::class, 'vip', [
    'vip' => true,
]);

$factory->state(Contact::class, 'vip', function (Faker\Generator $faker) {
    return [
        'vip' => true,
        'company' => $faker->company,
    ];
});

in use

$vip = factory(Contact::class)->state('vip')->create();

$vips = factory(Contact::class, 3)->state('vip')->create();
isopppisoppp

Databases and Eloquent ( Query Builder )

basics

// Basic statement
DB::statement('drop table users');

// Raw and parameter binding
DB::select(
    'select * from users where type = ?',
    [$type]
);
DB::select(
    'select * from users where type = :type',
    ['type' => $userType]
);
DB::insert(
    'insert into contacts (name, email) values (?, ?)',
    ['sally', 'sally@me.com']
);
DB::update(
    'update contacts set status = ? where id = ?',
    ['donor', $id]
);
DB::delete(
    'delete from contacts where archived = ?',
    [true]
);

// Select using the fluent builder
$users = DB::table('users')->get();

// Joins and other complex calls
DB::table('users')
    ->join('contacts', function ($join) {
        $join->on('users.id', '=', 'contacts.user_id')
             ->where('contacts.type', 'donor');
    })
    ->get();

more complex

// where where
DB::table('contacts')
    ->where('vip', true)
    ->where('created_at', '>', now()->subDay());

// orWhere
DB::table('contacts')
    ->where('vip', true)
    ->orWhere(function ($query) {
        $query->where('created_at', '>', now()->subDay())
            ->where('trial', false);
    })
    ->get();

// whereBetween
DB::table('drinks')
    ->whereBetween('size', [6, 12])
    ->get();

// whereIn
DB::table('contacts')
    ->whereIn('state', ['FL', 'GA', 'AL'])
    ->get();

// whereNull / whereNotNull
DB::table('contacts')
    -> whereNull('nullable')
    -> get();
DB::table('contacts')
    -> whereNotNull('nullable')
    -> get();

// whereRaw, *beware of sql injection, queries passed to whereRaw will not be escaped
DB::table('contacts')->whereRaw('id = 12345')->get()

// whereExists
DB::table('users')
    ->whereExists(function ($query) {
        $query->select('id')
            ->from('comments')
            ->whereRaw('comments.user_id = users.id');
    })
    ->get();

// distinct
DB::table('contacts')->select('city')->distinct()->get();

modifying methods

// orderBy
DB::table('contacts')
    ->orderBy('last_name', 'asc')
    ->get();

// groupBy
DB::table('contacts')
    ->groupBy('city')
    ->havingRaw('count(contact_id) > 30')
    ->get();

// skip / take
DB::table('contacts')->skip(30)->take(10)->get();

// latest(colName = 'created_at') / oldest (colName = 'created_at')
DB::table('contacts')->latest()>get(); // order by descending created_at
DB::table('contacts')->oldest()>get(); // order by ascending created_at

// inRandomOrder
DB::table('contacts')->inRandomOrder()>get();

returning methods

xxx->get(); // all results
xxx->first(); // first result (LIMIT 1 added), first will fail if result is empty
xxx->firstOrFail() // throw exception if result is empty
xxx->find(5) // automatically get by ID. also can use findOrFail()
xxx->value('email') // get value from a single fields from the first row
xxx->count() // count of all of the matching results
xxx->min('col') // return min value of col
xxx->max('col') // return max value of col
xxx->dd() // dump the underlying SQL query

transaction

DB::transaction(function () use ($userId, $numVotes) {
    // Possibly failing DB query
    DB::table('users')
        ->where('id', $userId)
        ->update(['votes' => $numVotes]);

    // Caching query that we don't want to run if the above query fails
    DB::table('votes')
        ->where('user_id', $userId)
        ->delete();
});

manual transaction

DB::beginTransaction();
if ($badThingsHappened) {
    DB::rollBack();
}
DB::commit();
isopppisoppp

Databases and Eloquent ( Eloquent )

the simplest model definition
php artisan make:model Contact with migration php artisan make:model Contact --migration (-m)

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;

class Contact extends Model
{
    use HasFactory;
}

customization

// The default behavior is like `SecondaryContact` should be `secondary_contacts`
protected $table = 'contacts_secondary';

// primary key, default is `id`
protected $primaryKey = 'contact_id';

// if need to disable autoincrement
public $incrementing = false;

// if don't need to add `careted_at` and `updated_at`
public $timestamps = false;

// date format customization ( default is probablly Datetime: Y-m-d H:i:s)
protected $dateFormat = 'U'; // Unix Timestamp

Handling Data with Eloquent

$allContacts = Contact::all();
$vipContacts = Contact::where('vip', true)->get();
$newestContacts = Contact::orderBy('created_at', 'desc') // or latest?
    ->take(10)
    ->get();
Contact::findOrFail($contactId)

chunk responses ( for memory usage or locking issue )

Contact::chunk(100, function ($contacts) {
    foreach ($contacts as $contact)  {
        // Do something with $contact
    }
});

aggregates

$countVips = Contact::where('vip', true)->count();
$sumVotes = Contact::sum('votes');
$averageSkill = User::avg('skill_level');

insert

$contact = new Contact;
$contact->name = 'Ken Hirata';
$contact->email = 'ken@hirata.com';
$contact->save();

// or

$contact = Contact::make([
    'name' => 'Ken Hirata',
    'email' => 'ken@hirata.com',
]);
$contact->save();

// or (immidiately save to database)
$contact = Contact::make([
    'name' => 'Ken Hirata',
    'email' => 'ken@hirata.com',
]); 

update

$contact = Contact::find(1);
$contact->email = 'natalie@parkfamily.com';
$contact->save();

definition of fillable or guarded variables

class Contact
{
    protected $fillable = ['name', 'email'];

    // or

    protected $guarded = ['id', 'created_at', 'updated_at', 'owner_id'];
}

filtering variable from request

Contact::create($request->only('name', 'email'));

firstOrCreate

$contact = Contact::firstOrCreate(['email' => 'luis.ramos@myacme.com']);

Deletion

$contact = Contact::find(5);
$contact->delete();

or

Contact::destroy(1);
Contact::destroy([1, 5, 7]);
Contact::where('updated_at', '<', now()->subYear())->delete();

soft delete

// migration
Schema::table('contacts', function (Blueprint $table) {
    $table->softDeletes();
});

// model
<?php

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;

class Contact extends Model
{
    use SoftDeletes; // use the trait

    protected $dates = ['deleted_at']; // mark this column as a date
}

// after that, delete() and destory() call will set the deleted_at

// retrieve not(?) soft deleted items
$allHistoricContacts = Contact::withTrashed()->get();

// retrieve soft deleted items
$deletedContacts = Contact::onlyTrashed()->get();

// to see whether instance has been soft-deleted or not
if ($contact->trashed()) {
    // do something
}

// restore item from soft-deleted state
$contact->restore();
Contact::onlyTrashed()->where('vip', true)->restore();

// force delete ( actually delete items from database )
$contact->forceDelete();
Contact::onlyTrashed()->forceDelete();
isopppisoppp

Databases and Eloquent ( Scope )

Add a local scope

class Contact
{
    public function scopeStatus($query, $status)
    {
        return $query->where('status', $status);
    }

use in query

$friends = Contact::status('friend')->get();

Add a global scope to a Model

class Contact extends Model
{
    protected static function boot()
    {
        parent::boot();

        static::addGlobalScope('active', function (Builder $builder) {
            $builder->where('active', true);
        });
    }

or create a global scope class

<?php

namespace App\Scopes;

use Illuminate\Database\Eloquent\Scope;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Builder;

class ActiveScope implements Scope
{
    public function apply(Builder $builder, Model $model)
    {
        return $builder->where('active', true);
    }
}

use in Model

<?php

use App\Scopes\ActiveScope;
use Illuminate\Database\Eloquent\Model;

class Contact extends Model
{
    protected static function boot()
    {
        parent::boot();

        static::addGlobalScope(new ActiveScope);
    }
}

remove global scopes in query

$allContacts = Contact::withoutGlobalScope('active')->get();
Contact::withoutGlobalScope(ActiveScope::class)->get();
Contact::withoutGlobalScopes()->get(); // disable all global scope
isopppisoppp

Databases and Eloquent ( Accessors / Mutators / Attribute Casting )

Accessors

The function name should be get{SampleName}Attribute

Handle empty attribute

// Model definition:
class Contact extends Model
{
    public function getNameAttribute($value)
    {
        return $value ?: '(No name provided)';
    }
}

// Accessor usage:
$name = $contact->name;

Define new a attribute which doesn't exist in the database

// Model definition:
class Contact extends Model
{
    public function getFullNameAttribute()
    {
        return $this->first_name . ' ' . $this->last_name;
    }
}

// Accessor usage:
$fullName = $contact->full_name;

Mutators

The mutator name should be set{SampleName}Attribute

// Defining the mutator
class Order extends Model
{
    public function setAmountAttribute($value)
    {
        $this->attributes['amount'] = $value > 0 ? $value : 0;
    }
}

// Using the mutator
$order->amount = '15';

proxy column, this is uncommon method because it can be confusing

// Defining the mutator
class Order extends Model
{
    public function setWorkgroupNameAttribute($workgroupName)
    {
        $this->attributes['email'] = "{$workgroupName}@ourcompany.com";
    }
}

// Using the mutator
$order->workgroup_name = 'jstott';

Attribute Casting

class Contact
{
    protected $casts = [
        'vip' => 'boolean',
        'children_names' => 'array',
        'birthday' => 'date',
    ];
}

Date Mutators

class Contact
{
    protected $dates = [
        'met_at',
        // already added created_at and updated_at by default
    ];
}
isopppisoppp

Databases and Eloquent ( Eloquent Serialization )

two types of serializer

$contactArray = Contact::first()->toArray();
$contactJson = Contact::first()->toJson();
$contactsArray = Contact::all()->toArray();
$contactsJson = Contact::all()->toJson();

The result of converting Eqloquent instance or string to string is the same as toJson
And every routes try to return result as string, so we can easily return Json response like

// routes/web.php
Route::get('api/contacts', function () {
    return Contact::all();
});

Route::get('api/contacts/{id}', function ($id) {
    return Contact::findOrFail($id);
});

Hiding attributes from json

class Contact extends Model
{
    public $hidden = ['password', 'remember_token'];
    // or 
    public $visible = ['name', 'email', 'status'];

Mannually change visibility

$array = $user->makeVisible('remember_token')->toArray();
isopppisoppp

Databases and Eloquent ( Eloquent Relationship )

One to One

// class def
class Contact extends Model
{
    public function phoneNumber()
    {
        return $this->hasOne(PhoneNumber::class);
        return $this->hasOne(PhoneNumber::class, 'owner_id'); // if you want to use 'owner_id’, not 'contact_id'
    }

// usage
$contact = Contact::first();
$contactPhone = $contact->phoneNumber;

// inverse
class PhoneNumber extends Model
{
    public function contact()
    {
        return $this->belongsTo(Contact::class);
    }

$contact = $phoneNumber->contact;

inserting related items

$contact = Contact::first();

$phoneNumber = new PhoneNumber;
$phoneNumber->number = 8008675309;
$contact->phoneNumbers()->save($phoneNumber);

// or

$contact->phoneNumbers()->saveMany([
    PhoneNumber::find(1),
    PhoneNumber::find(2),
]);

// or

$contact->phoneNumbers()->create([
    'number' => '+13138675309',
]);

// or

$contact->phoneNumbers()->createMany([
    ['number' => '+13138675309'],
    ['number' => '+15556060842'],
]);

One to Many

class User extends Model
{
    public function contacts()
    {
        return $this->hasMany(Contact::class);
    }

// usage
$user = User::first();
$usersContacts = $user->contacts;

// filtering
$donors = $user->contacts->filter(function ($contact) {
    return $contact->status == 'donor';
});

$lifetimeValue = $contact->orders->reduce(function ($carry, $order) {
    return $carry + $order->amount;
}, 0);

// inverse
class Contact extends Model
{
    public function user()
    {
        return $this->belongsTo(User::class);
    }
$userName = $contact->user->name;

attach and detach related item

$contact = Contact::first();

$contact->user()->associate(User::first());
$contact->save();

$contact->user()->dissociate();
$contact->save()

Selecting only records that have a related item

$postsWithComments = Post::has('comments')->get();
$postsWithManyComments = Post::has('comments', '>=', 5)->get();
$usersWithPhoneBooks = User::has('contacts.phoneNumbers')->get();

// Gets all contacts with a phone number containing the string "867-5309"
$jennyIGotYourNumber = Contact::whereHas('phoneNumbers', function ($query) {
    $query->where('number', 'like', '%867-5309%');
});

Has many through

class User extends Model
{
    public function phoneNumbers()
    {
        return $this->hasManyThrough(PhoneNumber::class, Contact::class);
    }

Has one through

class User extends Model
{
    public function phoneNumber()
    {
        return $this->hasOneThrough(PhoneNumber::class, Company::class);
    }

Many to Many

// pivot table should be contact_user

class User extends Model
{
    public function contacts()
    {
        return $this->belongsToMany(Contact::class);

        // or
        return $this->belongsToMany(Contact::class)
            ->withTimestamps()
            ->withPivot('status', 'preferred_greeting');
    }
}

class Contact extends Model
{
    public function users()
    {
        return $this->belongsToMany(User::class);
    }
}
isopppisoppp

Databases and Eloquent ( Eloquent Relationship - Polymorphic )

この章疲れたので今度 -> 5. Databases and Eloquent の polymorphic

isopppisoppp

Databases and Eloquent ( Eager Loading )

Eager Loading

$contacts = Contact::with('phoneNumbers')->get();
$contacts = Contact::with('phoneNumbers', 'addresses')->get(); // multiple
$authors = Author::with('posts.comments')->get(); // nested
$contacts = Contact::with(['addresses' => function ($query) {
    $query->where('mailable', true);
}])->get(); // not all relations

Lazy eager loading

// lazy eager loading
$contacts = Contact::all();

if ($showPhoneNumbers) {
    $contacts->load('phoneNumbers');
    $contacts->loadMissing('phoneNumbers'); // recommend
}

Eager Loading only the count

// Adds a "posts_count" integer to each Author with a count of that
// author's related posts
$authors = Author::withCount('posts')->get();
isopppisoppp

Databases and Eloquent ( Eloquent Events )

class AppServiceProvider extends ServiceProvider
{
    public function boot()
    {
        $thirdPartyService = new SomeThirdPartyService;

        Contact::creating(function ($contact) use ($thirdPartyService) {
            try {
                $thirdPartyService->addContact($contact);
            } catch (Exception $e) {
                Log::error('Failed adding contact to ThirdPartyService; canceled.');

                return false; // Cancels Eloquent create()
            }
        });
    }

available events

https://laravel.com/docs/7.x/eloquent#events

isopppisoppp

Collecting and Handling User Data

Route::post('form', function (Illuminate\Http\Request $request) {
    // $request->etc()
});

except/only

Route::post('post-route', function (Request $request) {
    var_dump($request->except('_token'));
});
Route::post('post-route', function (Request $request) {
    var_dump($request->only(['firstName', 'utm']));
});

has

if ($request->has('utm')) {
    // Do some analytics work
}

input

Route::post('post-route', function (Request $request) {
    $userName = $request->input('name', 'Matt');
});

array input

Route::post('employees', function (Request $request) {
    $employeeZeroFirstName = $request->input('employees.0.firstName');
    $allLastNames = $request->input('employees.*.lastName');
    $employeeOne = $request->input('employees.1');
    var_dump($employeeZeroFirstname, $allLastNames, $employeeOne);
});

file data

Route::post('form', function (Request $request) {
    if ($request->hasFile('profile_picture')) {
        var_dump($request->file('profile_picture'));
    }
});

Validation

Manual Validation

Route::post('recipes', function (Illuminate\Http\Request $request) {
    $validator = Validator::make($request->all(), [
        'title' => 'required|unique:recipes|max:125',
        'body' => 'required'
    ]);

    if ($validator->fails()) {
        return redirect('recipes/create')
            ->withErrors($validator)
            ->withInput();
    }

    // Recipe is valid; proceed to save it
});

Custom Rule ( php artisan make:rule RuleName )

class WhitelistedEmailDomain implements Rule
{
    public function passes($attribute, $value)
    {
        return in_array(Str::after($value, '@'), ['tighten.co']);
    }

    public function message()
    {
        return 'The :attribute field is not from a whitelisted email provider.';
    }
}

// in use
$request->validate([
    'email' => new WhitelistedEmailDomain,
]);

Form Requests

php artisan make:request CreateCommentRequest

<?php

namespace App\Http\Requests;

use App\BlogPost;
use Illuminate\Foundation\Http\FormRequest;

class CreateCommentRequest extends FormRequest
{
    public function authorize()
    {
        $blogPostId = $this->route('blogPost');

        return auth()->check() && BlogPost::where('id', $blogPostId)
            ->where('user_id', auth()->id())->exists();
    }

    public function rules()
    {
        return [
            'body' => 'required|max:1000',
        ];
    }
}

// in use, specify the class
Route::post('comments', function (App\Http\Requests\CreateCommentRequest $request) {
    // Store comment
});
isopppisoppp

Artisan and Tinker

Artisan

  • clear-compiled: Remove Laravel's compiled class file
  • down up: Handle maintenance mode
  • env: Echo app()->environment() in-app
  • migrate: Runs all database migrations
  • optimize: Clears and refreshes the configuration and route files
  • preset: Changes out the frontend scaffolding for another
  • serve: Pins up a PHP server at localhost:8000
  • tinker: Brings up the Tinker REPL.
  • --env Specify env (local, production, etc.)

grouped commands

  • auth ( auth:clear-resets )
  • cache (cache:clear cache:forget cache:table )
  • config ( config:cache config:clear )
  • db ( db:seed )
  • event ( event:list event:cache event:clear )
  • key ( key:generate )
  • make
  • migrate ( migrate:install migrate:reset migrate:refresh migrate:fresh migrate:status )
  • notifications ( notifications:table generates )
  • queue ( queue:listen queue:table )
  • route ( route:list route:cache )
  • schedule ( schedule:run )
  • session ( session:table )
  • storage ( storage:link )
  • vendor ( vendor:publish )
  • view ( view:clear )

custom artisan command

php artisan make:command WelcomeNewUsers --command=email:newusers

<?php

namespace App\Console\Commands;

use Illuminate\Console\Command;

class WelcomeNewUsers extends Command
{
    /**
     * The name and signature of the console command
     *
     * @var string
     */
    protected $signature = 'email:newusers';

    /**
     * The console command description
     *
     * @var string
     */
    protected $description = 'Command description';

    /**
     * Create a new command instance
     *
     * @return void
     */
    public function __construct()
    {
        parent::__construct();
    }

    /**
     * Execute the console command
     *
     * @return mixed
     */
    public function handle()
    {
        //
    }
}

Dump Server

php artisan dump-server

Route::get('/', function () {
    dump('Dumped Value');

    return 'Hello World';
});
isopppisoppp

Testing

./vendor/bin/phpunit: target files are test/**/*Test.php

このスクラップは2020/12/22にクローズされました