このビデオがターミナルのカスタマイズからAuthまで網羅してえらく長かった(6時間!)けどよかったです。途中何度も寝落ちしながらなんとか最後まで見ました。
Complete Laravel Tutorial | Laravel From Scratch | Full Laravel Course | Laravel For Beginners
動画観ながらメモったものをそのままずらっと残しておきます。
composer create-project --prefer-dist laravel/laravel firstproject --- iTerm2 --- php artisan serve Ctrl+Z php artisan serve --port=8081 --- sudo nano /etc/hosts 127.0.0.1 firstproject.test --- Visual Studio Code Atom PHPStorm Sublime Text --- Visual Studio Code extensions Laravel Artisan Laravel Blade Snippets Laravel Blade Spacer Laravel goto view Laravel Snippets Laravel Extra Intellisense Live Sass Compiler Beautify css/sass/scss/less PHP Intellisense Getter and Setter Generator (created by Augustin Bracket Pair Colorizer Emmet Live GitHub (created by KnisterPeter Javascript (ES6) code snippets VScode Great Icons --- PHPstorm (58:55) extensions Laravelのみ? --- VSCode Shift + Cmd + P 「file icon theme」 VSCode Great Icons --- Http/Controllers Models Resources/css Resources/js Resources/views database/ routes/ storage/ --- .env routes/web.php Route::get('/', function () { return view('welcome'); }); .env CREATOR_NAME=Shiraishi routes/web.php Route::get('/', function () { return env('CREATOR_NAME'); }); -- composer require ">= 4 < 5" require-dev でも動くけど、クラッシュするほど必要なわけではない開発中の試しみたいなもの composer.lockは、実際にインストールされた状態 composer.lockを削除して、再度、composer installを実行すると更新されて、またlockファイルが作成される --- Packagist composerに記述することなく、記述してインストールしたかのように追加できる packagist.orgにて mollieを検索 composer require mollie/mollie-api-php を実行するだけ --- composer show --tree composer dump-autoload --- Basic Route routes/web.php Route::get('/', function () { return view('welcome'); }); ↓ resources/views/welcome.blade.php //JSON Route::get('/', function () { return response()->json([ 'hoge' => 'aaa', 'fuga' => 'bbb' ]); }); //function Route::get('/users', function () { return redirect('/'); }); ---- php artisan list php artisan make:controller ProductsController php artisan make:controller ProductsController --force (強制上書き) web.php use App\Http\Controllers\ProductsController; Route::get('/products', [ProductsController::class, 'index']); or Route::get('/products', 'App\Http\Controllers\ProductsController@index']); before Laravel 8 Route::get('/products', 'ProductsController@index']); → error ProductsController.php class ProductsController extend Controller { public function index(){ return view('products.index'); } } (view) resources/views/products/index.blade.php doc+TAPキー(VSCode) ----- Passing Data to View ProductsController.php class ProductsController extend Controller { public function index(){ $title = 'Hello Laravel 8'; $desc = 'hoge hoge hoge hoge' //return view('products.index', compact('title', 'desc')); return view('products.index')->with('title', $title); } } index.blade.php {{ $title }} {{ $desc }} - ProductsController.php class ProductsController extend Controller { public function index(){ $title = 'Hello Laravel 8'; $desc = 'hoge hoge hoge hoge' $data = [ 'productOne' => 'iPhone', 'productTwo' => 'Android' ] return view('products.index')->with('data', $data); or return view('products.index', [ 'data' => $data ]); } } index.blade.php @foreach($data as $item) <p>{{ $item }}</p> @endforeach ---- web.php use App\Http\Controllers\ProductsController; Route::get('/products/{id}', [ProductsController::class, 'show']); public function show($id){ return $id; } -- Route::get('/products/{name}', [ProductsController::class, 'show']); public function show($name){ $data = [ 'iphone' => 'iPhone', 'samsung' => 'Samsung' ]; return view('products.index', [ 'products' => $data[$name] ?? 'Product '.$name.' does not exist.'; ]); } index.blade.php <p>{{ $products }}</p> -- Pattern Route::get('/products/{id}', [ProductsController::class, 'show'])->where('id', '[0-9]+'); Route::get('/products/{name}', [ProductsController::class, 'show'])->where('name', '[a-zA-Z]+'); Route::get('/products/{name}{id}', [ProductsController::class, 'show'])->where([ 'name' => '[a-z]+', 'id' => '[0-9]+' ]); -- ルート定義に名前をつける? Route::get('/products/{id}', [ProductsController::class, 'index'])->name('products'); <a href="{{ route('products') }}">Product</a> ---- VIEW views layout header.blade.php app.blade.php footer.blade.php --app.blade.php <header> @include('layouts.header') </header> @yield('content') <footer> @include('layouts.footer') </footer> --index.blade.php @extends('layouts.app') @section('content') indexの内容 @endsection --about.blade.php @extends('layouts.app') @section('content') aboutの内容 @endsection --header.blade.php <a href="/" class='{{ request()-is('/') ? 'active' : '' }}'>Home</a> <a href="about" class='{{ request()-is('about/*') ? 'active' : '' }}'>About</a> ---- Asset public/images {{ print_r(URL()) }} <img src="{{ URL('images/hoge.jpg') }}" > -- Storage php artisan storage:link rootにstorageへのシンボリックリンクを作成 <img src="{{ URL('storage/hoge.jpg') }}" > <img src="{{ asset('storage/hoge.jpg') }}" > ------ VIEW(blade.php) @[structure] {{ $name }} @[endstructure] @if() @elseif() @endif @unless(empty($name)) @endunless @if(!empty($name)) @endif @isset($name) @endisset @swith (swithと入力してTAP) For loop Foreach loop For else loop While loop @for ($i = 0; $i < 10; $i++ ) <h2>The number is {{ $i }}</h2> @endfor @foreach ($names as $name) <h2>The name is {{ $name }}</h2> @endforeach @forelse ($names as $name) <h2>The name is {{ $name }}</h2> @empty <h2>There are no names!</h2> @endforelse {{ $i = 0 }} @while ($i < 10) <h2>{{ $i }}</h2> {{ $i++ }} @endwhile ----- Compiling Assets (2:50:00) webpack.mix.js sassに書き換え mix.js('resources/js/app.js', 'public/js') .sass('resources/scss/app.scss', 'public/css', [ // ]); npm install(必要なものがインストールされる) npm run dev npm run dev(もう一度) (その後の自動化は失敗。PHPStormのFile Watcherがエラー・・) ---- Laravel Frontend Preset 2:59:15 php artisan present react php artisan present bootstrap 「Laravel frontend presets」で検索 Tailwindcsscomposer require laravel-frontend-presets/tailwindcss --dev ---- #27 Databases & MIgrations php artisan make:migration create_posts_table php artisan make:model Post -m Created Migration: 2021_10_28_121339_create_posts_table.php public function up() { Schema::create('posts', function (Blueprint $table) { $table->increments('id'); $table->string('title'); $table->mediumText('body'); $table->timestamps(); }); } php artisan migrate mysql -u root -p {password} mysql> show databases; mysql> use _databasename; mysql> show tables; mysql> desc users; php artisan migrate:reset php artisan migrate:refresh php artisan migrate:fresh php artisan migrate:rollback php artisan migrate:status -- Factory php artisan make:factory PostFactory use Illuminate\Support\Str; public function definition() { return [ 'title' => $this->faker->title, 'body' => $this->faker->paragraph, 'created_at' => now() ]; } php artisan tinker Psy Shell v0.10.9 >>> \App\Models\Post::factory()->count(2)->create(); class ProductsController extends Controller { public function index(){ $posts = DB::select('select * from posts'); dd($posts); } } ----- Query Builder (3:23:23) $posts = DB::select('select * from posts WHERE id = ?', [7]); $posts = DB::select('select * from posts WHERE id = :id', ['id' => 7]); $posts = DB::table('posts') ->where('id', $id) ->get(); $posts = DB::table('posts') ->select('body') ->get(); $posts = DB::table('posts') ->where('created_at', '>', now()->subDay()) ->get(); $posts = DB::table('posts') ->where('created_at', '>', now()->subDay()) ->orWhere('title', 'Prof') ->get(); $posts = DB::table('posts') ->whereBetween('id', [7, 9]) ->get(); $posts = DB::table('posts') ->whereNotNull('title') ->get(); $posts = DB::table('posts') ->whereRaw('title') ->get(); $posts = DB::table('posts') ->select('title') ->distinct() ->get(); $posts = DB::table('posts') ->orderBy('title', 'asc') ->get(); $posts = DB::table('posts') ->latest() ->get(); $posts = DB::table('posts') ->oldest() ->get(); $posts = DB::table('posts') ->inRandomOrder() ->get(); $posts = DB::table('posts') ->orderBy('created_at', 'desc') ->first(); $posts = DB::table('posts') ->orderBy('created_at', 'desc') ->first(); $id = 7; $posts = DB::table('posts') ->find($id); $posts = DB::table('posts') ->where('id', $id); ->count(); $posts = DB::table('posts') ->count(); $posts = DB::table('posts') ->min('id'); $posts = DB::table('posts') ->max('id'); $posts = DB::table('posts') ->sum('id'); $posts = DB::table('posts') ->avg('id'); $posts = DB::table('posts') ->insert([ 'title' => 'New Post', 'body' => 'New Body' ]); $posts = DB::table('posts') ->where('id', '=', 15 ) ->update([ 'title' => 'Update Post', 'body' => 'Updated Body' ]); $posts = DB::table('posts') ->where('id', '=', 15 ) ->delete(); ------- #30 Eloquent (3:40:35) 3:59:42 ----- #32 Relations One To Many php artisan make:model Car -m -- CreateCarsTable public function up() { Schema::create('cars', function (Blueprint $table) { $table->increments('id'); $table->string('name'); $table->integer('founded'); $table->longText('description'); $table->timestamps(); }); Schema::create('car_models', function (Blueprint $table) { (CreateCarTableで同時に作ってる例 $table->increments('id'); $table->unsignedInteger('car_id'); $table->string('model_name'); $table->timestamps(); $table->foreign('car_id') ->references('id') ->on('cars') ->onDelete('cascade'); }); } php artisan make:model CarModel -m (CarModel で car_modelsテーブル) 上記CreateCarTableで作ってる場合 php artisan make:model CarModel php artisan make:model Engine -m -- CreateEnginesTable public function up() { Schema::create('engines', function (Blueprint $table) { $table->increments('id'); $table->unsignedInteger('model_id'); $table->string('engine_name'); $table->timestamps(); $table->foreign('model_id') ->references('id') ->on('car_models') ->onDelete('cascade'); }); } mysql -u root -p {password} mysql> use _databasename; mysql> show tables; mysql> desc users; --モデルに関係性を定義 class Car extends Model { use HasFactory; protected $table = 'cars'; protected $primaryKey = 'id'; protected $fillable = ['name', 'founded', 'description']; public function carModels() { return $this->hasMany(CarModel::class); } } carModels() (キャメルケースで) class CarModel extends Model { use HasFactory; protected $table = 'car_models'; protected $primaryKey = 'id'; public function car(){ return $this->belongsTo(Car::class); } } show.blade.php @forelse ($car->carModels as $model) <li> {{ $model['model_name'] }} </li> @empty <p> No models found </p> @endforelse #33 HasMany & HasSome Has One(4:59:23) Schema::create('car_production_dates', function (Blueprint $table) { $table->id('id'); $table->unsignedInteger('model_id'); date('created_at'); $table->foreign('model_id') ->references('id') ->on('car_models') ->onDelete('cascade'); }); public function engines() { return $this->hasManyThrough() { Engine::class, CarModel::class, 'car_id', //Foreign key on CarModel table 'model_id' //Foreign key on Engine table } } Public funtion productionDate() { return $this-hasOneThrough( CarProductionDate::class, CarModel::class, 'car_id', 'model_id' ) } <td> {{ date('d-m-Y', strtotime($car->productionDate->created_at)) }} </td> Many To Many(5:04:57) PIVOT TABLE(中間テーブル) Table: cars Pivot Table: car_product car_id : product_id Table: products php artisan make:model Product -m (5:06:49) Schema::create('products', function (Blueprint $table) { $table->increments('id'); $table->string('name'); $table->timestamps(); }); php artisan make:model CarProduct -m (5:07:43) Schema::create('car_products', function (Blueprint $table) { $table->integer('car_id')->unsigned(); $table->integer('product_id')->unsigned(); $table->foreign('car_id') ->references('id') ->on('cars') ->onDelete('cascade'); $table->foreign('product_id') ->references('id') ->on('products') ->onDelete('cascade'); }); php artisan migrate class Product extends Model { use HasFactory; public function cars() { return $this->belongsToMany(Car::class) ] } CarController.php public function show($id) { $car = Car::find($id) $product = Product::find($id); return view('cars.show')->with('car', $car); } ---- #35 Accessing the Request (5:15:04) CarController.php $request->validate([ 'name' => 'required|unique:cars', 'founded' => 'required|integer|min:0|max:2021', 'description' = 'required' ]) create.blade.php @if ($errors->any()) <div> @foreach ($errors->all() as $error) <li class="text-red-500 list-none"> {{ $error }} </li> @endforeach </div> @endif --- カスタムルール php artisan make:rule Uppercase public function passes($attribute, $value) { return strtoupper($value) === $value; } CarController.php use App\Rules\Uppercase; $request->validate([ 'name' => new Uppercase, 'founded' => 'required|integer|min:0|max:2021', 'description' = 'required' ]) $request->validate([ 'name' => ['required', 'unique:cars', new Uppercase], ←配列で指定? 'founded' => 'required|integer|min:0|max:2021', 'description' = 'required' ]) ----- #37 FORM REQUESTS (5:36:15) カスタムリクエスト php artisan make:request MyRequest App\Http\Requests\MyRequest; public function authorize(){ return true; } public function rules() { return [ 'name' => 'required|unique:cars', 'founded' => 'required|integer|min:0|max:2021', 'description' = 'required' ]; } CarController.php use App\Http\Requests\MyRequest; public function store(MyRequest $request) { $request->validated(); public function update(MyRequest $request) { $request->validated(); ------ #38 Image Upload (5:41:06) テーブルにフィールドを追加 php artisan make:migration add_image_to_cars_table up() $table->string('image_path'); down() $table->dropColumn('image_path'); dd($request->all()); イメージのバリデーション $request->validate([ 'image' => 'required|mimes:jpg,png,jpeg|max:5048' ]); $test = $request->file('image')->guessExtension(); dd($test); guessExtension() getMimeType() store() asStore() storePublicly() move() getClientOriginalName() getClientMimeType() guessClientExtension() getSize() getError() → エラーなし0 isValid() → 正常true $newImageName = time().'-'.request->name.'.'.$request->image->extension(); dd(newImageName); > 1611790072-Mercedes.jpg $test = $request->image->move(public_path('images'), $newImageName); <img src="{{ asset('images/'.$car->image_path) }}" > ------- #39 ARTISAN COMMANDS (6:00:15) php artisan list php artisan clear-compiled php artisan down php artisan up php artisan env php artisan help php artisan --version php artisan --env php artisan optimize php artisan cache:clear php artisan auth:clear-resets(リメンバートークンをクリア) php artisan key:generate(APP_KEYをリフレッシュ) php artisan session:table php artisan view:clear ----- #40 Auth composer require laravel-frontend-presets/tailwindcss --dev php artisan ui tailwindcss php artisan ui tailwindcss --auth npm install && npm run dev php artisan route:clear php artisan optimize php artisan route:list index.blade.php {{ dd(Auth:user()) }} @if (isset(Auth:user()->id) && Auth:user()->id == $car->user_id) <form>edit delete</form> @endif FOR non-logged in users public function __contruct() { $this->middleware('auth', ['except' => ['index', 'show']) }A Tailwind CSS frontend preset for the Laravel Framework - laravel-frontend-presets/tailwindcss
東京造形大学卒業後、マクロメディア(現アドビ)に入社。QAやテクニカルサポートマネージャーとしてFlash、DreamweaverなどのWeb製品を担当。独立後、2007年に虫カゴデザインスタジオ株式会社を設立。2021年東京三鷹を拠点に。最近は、Unity, Unity Netcode for GameObjects, CakePHP, Laravel, ZBrush, Modo, Adobe Substance 3D, Adobe Firefly, Xcode, Apple Vision Pro, Firebaseにフォーカスしています。モバイルアプリ開発情報を主としたブログ「MUSHIKAGO APPS MEMO」の中の人。