# Laravel Project ###### tags: `laravel` `web` > [TOC] ## Routing ### Response Response -> directly response web page In response function, we could put html code in function ```php Route::get("/", function (){ return response( "<h1> meowhecker </h1> <br/> <p>阿晟好帥</p>" ); }); ``` ![](https://i.imgur.com/NwEVqaE.png) ### Carry the header Also we could carry the header ```php= //response carray header (text plain) Route::get("/responseCarrayHeader", function (){ return response( "<h1> meowhecker </h1> <br/> <p>阿晟好帥</p>" ,404 )->header('content-Type', 'text/plain'); // It won't execute html }); ``` ![](https://i.imgur.com/Linz973.png) ### Wildcard Wildcard just a curly brace {} Function variable pass vale to wildcard ```php= //wildcard get value from route Route::get("/wildCard/{password}", function($password){ return response("meowhckerPassword is ". $password ); }); ``` ![](https://i.imgur.com/V1kRov7.png) ### Router Constraint ```php= //Router constraints Route::get("/routeConstraint/{id}", function($id){ return response( "meowhecker phone is ". $id); })->where('id','[0-9]+'); //constratin number which are nubmers. ``` ![](https://i.imgur.com/FhQafVw.png) > If you input error form ![](https://i.imgur.com/h3cEba0.png) ## Debug ### Did dump helper ```php= dd(); ddd(); ``` ## Request ```php //Request Route::get("/search", function(Request $request){ dd($request); }); ``` ![](https://i.imgur.com/l5CxunA.png) Query ![](https://i.imgur.com/JsFi11E.png) ### Access values directly $request->Name ```php //Request Route::get("/search", function(Request $request){ //?name = meowhecker //use way $request->Name //dd($request->name. $request->password); return $request->name . $request->password; }); ``` ![](https://i.imgur.com/DcW7h7H.png) ![](https://i.imgur.com/UdApS0d.png) ## API router Route-> api/<fileRouter> ```php= Route::get("/posts", function(){ return response()->json([ "post"=> [ "title"=>"meow" ] ]); }); ``` ![](https://i.imgur.com/xpZr3lY.png) ## View & pass the value ### Display the view ![](https://i.imgur.com/y0z4QVE.png) ```php= Route::get('/', function () { return view('listings'); }); ``` ### Pass the value view() function second arg use to pass the value ```php= Route::get('/', function () { return view('listings',['data'=>"test"]); //second args -> pass the value. }); ``` And then, we could use php to show it ```html= <h1>meowhecker</h1> <?php echo $data ?> ``` Result ![](https://i.imgur.com/aWQDgHn.png) ### Show array data Using foreach to implement it ```php= Route::get('/', function () { return view('listings', [ 'heading'=>"meowhecker website 2.0", 'listings'=>[ [ 'id' => 1, 'title' => "text1", 'description'=>"meowmeow" ], [ 'id' => 2, 'title' => "text2", 'description'=>"meowmeowmeowh" ] ] ] ``` ```php= <h1>meowhecker</h1> <?php //echo $data; ?> <?php foreach($listings as $listing):?> <h2><?php echo $listing["title"];?></h2> <h2><?php echo $listing["description"];?></h2> <?php endforeach;?> ``` ![](https://i.imgur.com/6EbV4CP.png) ## Blade Templates && Basic Directory Blade templates let code more clean and easily. ### Use double braces instead with php tags ```php= <h1>meowhecker</h1> <?php //echo $data; ?> {{$heading}} <?php foreach($listings as $listing):?> <h2><?php echo $listing["title"];?></h2> <h2><?php echo $listing["description"];?></h2> <?php endforeach;?> ``` ![](https://i.imgur.com/pVFoIFa.png) ### @ scope directer ```php= <h1>meowhecker</h1> <?php //echo $data; ?> {{$heading}} @foreach($listings as $listing) <h2>{{echo $listing["title"]}}</h2> <h2>{{php echo $listing["description"]}}</h2> @endforeach ``` ### php directive if you want to filling something, but you can't filter it by using the controller or the router. So, we could use php directive to filter it. ```php= <h1>meowhecker</h1> <?php //echo $data; ?> {{$heading}} <!-- php directive --> @php $name ="meowhecker"; echo $name; @endphp @foreach($listings as $listing): <h2>{{$listing["title"]}}</h2> <h2>{{$listing["description"]}}</h2> @endforeach ``` ### Eloquent Model To solve hard codes which store data in a route So, we through model Eloquent which is an Rom object. it cant give our eloquent ways to deal with our database Give a class name as model class, we could use it to instead with pass values in the view function After, we create the model, we could migrate it. And it will auto create the table on your database. ### self-created Model Declare the namespace (App/models Create a class static all()function ****Model**** ```php= <?php namespace App\Models; class Listing{ //return all data public static function all(){ return [ [ 'id' => 1, 'title' => "text1", 'description'=>"meowmeow" ], [ 'id' => 2, 'title' => "text2", 'description'=>"meowmeowmeow" ] ]; } } ?> ``` ****Route**** We have to invoke App/Models use App/Models/className Listings::all() (fetch all data ```php= Route::get('/', function () { return view('listings', [ 'heading'=>"meowhecker website 2.0", 'listings'=>Listing::all() ] ); //second args -> pass the value. }); ``` #### Find function if we want just to show the specific content, we could use id to implement it. ## DataBase setup [SQL cheat sheet](https://gist.github.com/bradtraversy/c831baaad44343cc945e76c2e30927b3) ``` mysql -u <userName> -p <password> ``` ``` show databases; create database <DBname> ``` env file ``` DB_CONNECTION=mysql DB_HOST=127.0.0.1 DB_PORT=3306 DB_DATABASE=postweb2 DB_USERNAME=meowhecker DB_PASSWORD=meowhecker ``` ## Database Migrations Migration was used to create the table and columns. So, we were able to create ones without typing commands line one by one. We don't have to manually create it. We can use artisan ``` php artisan make:migration create_listings_table ``` Add the fields we're going to want ```php= public function up() { Schema::create('listings', function (Blueprint $table) { $table->id(); $table->string("name"); $table->string("expertiseSkill"); $table->string("interests"); $table->string("location"); $table->string("email"); $table->longText("description"); $table->timestamps(); }); } ``` ### Run migrate (create the table and columns ``` php artisan migrate ``` ![](https://i.imgur.com/XBXFlAQ.png) ![](https://i.imgur.com/LAFPyk5.png) ### Roll Back Database [Drop table by the command](https://stackoverflow.com/questions/31229193/how-to-drop-table-in-laravel) You can also rollback if you want to drop your last migrated table ``` php artisan migrate:rollback ``` ## Seeding data and factories (Default) Usually, we were going to used it for quickly get stuff in database. And then we could test things with ### Database Seeding Seeder/DatabaseSeeder -> factories/UserFactory.php ``` php artisan db:seed ``` Produce dumpy users by the factory ![](https://i.imgur.com/dTkWaAf.png) ### Rid of data form database ``` php artisan migrate:refresh ``` ### Rid and seed ``` php artisan migrate:refresh --seed ``` ## Eloquent Model Eloquent Model use the command to establish ``` php artisan make:model Listings ``` ### Seeding Database we could use Create Method of Eloquent Model to crate data into DB. DatabaseSeeder.php ```php= <?php namespace Database\Seeders; // use Illuminate\Database\Console\Seeds\WithoutModelEvents; use App\Models\Listing; use Illuminate\Database\Seeder; class DatabaseSeeder extends Seeder { /** * Seed the application's database. * * @return void */ public function run() { \App\Models\User::factory(5)->create(); // \App\Models\User::factory()->create([ // 'name' => 'Test User', // 'email' => 'test@example.com', // ]); //put data into the table Listing::create( [ 'name' => 'Laravel Senior Developer', 'expertiseSkill' => 'laravel, javascript', 'interests' => 'Acme Corp', 'location' => 'Boston, MA', 'email' => 'email1@email.com', 'website' => 'https://www.acme.com', 'description' => 'Lorem ipsum dolor sit amet consectetur adipisicing elit. Ipsam minima et illo reprehenderit quas possimus voluptas repudiandae cum expedita, eveniet aliquid, quam illum quaerat consequatur! Expedita ab consectetur tenetur delensiti?' ] ); } } ``` ![](https://i.imgur.com/jciCHqV.png) ## Create Factory ``` php artisan make:factory ListngFactory ``` Using factory creates listing contents ListingsFactory.php ```php= <?php namespace Database\Factories; use Illuminate\Database\Eloquent\Factories\Factory; /** * @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\Listing> */ class ListingFactory extends Factory { /** * Define the model's default state. * * @return array<string, mixed> */ public function definition() { return [ 'name' => $this->faker->name(), 'expertiseSkill' => 'security, web develope, IOT', 'interests' => $this->faker->sentence(), 'location' => $this->faker->city(), 'email' => $this->faker->email(), 'website' => $this->faker->url(), 'description' => $this->faker->paragraph(10), ]; } } ``` DatabasSeeeder.php ```php= <?php namespace Database\Seeders; // use Illuminate\Database\Console\Seeds\WithoutModelEvents; use App\Models\Listing; use Illuminate\Database\Seeder; class DatabaseSeeder extends Seeder { /** * Seed the application's database. * * @return void */ public function run() { //put data into the table //USER \App\Models\User::factory(5)->create(); // \App\Models\User::factory()->create([ // 'name' => 'Test User', // 'email' => 'test@example.com', // ]); //LISTINGS // Listing::create( // [ // 'name' => 'Laravel Senior Developer', // 'expertiseSkill' => 'laravel, javascript', // 'interests' => 'Acme Corp', // 'location' => 'Boston, MA', // 'email' => 'email1@email.com', // 'website' => 'https://www.acme.com', // 'description' => 'Lorem ipsum dolor sit amet consectetur adipisicing elit. Ipsam minima et illo reprehenderit quas possimus voluptas repudiandae cum expedita, eveniet aliquid, quam illum quaerat consequatur! Expedita ab consectetur tenetur delensiti?' // ] // ); // Use factory to creaet listings contents Listing::factory(5)->create(); } } ``` Terminal ``` php artisan migrate:refresh --seed ``` ![](https://i.imgur.com/cvNyEj2.png) ## Layout and Sections >layout put every pages which demand stuffs e.g html header or CSS style layout.blade.php ```php= <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>阿晟 Web</title> </head> <body> {{--view output--}} @yield('viewContents'); </body> </html> ``` ``` @extends('layout') @section('viewContents') {{--yield section--}} (view contents) @endsection ``` ## Adding HTML theme If we use eloquent model we could use $variable->fieldName replace the $variable["fieldName"] ### Asset Helper asset() in laravel is used to redirect to the public folder. blade.php ``` src="{{asset('images/no-image.png')}}" {{--asset() redirect to the public folder--}} ``` ## Template partials If you want to dived web page to the many of parts You could put it in the folder, and use include function to call it. ``` @include('partials.search') ``` partials are the folder where is in the view folder. ## Router Model Binding since we use eloquent model, we could use something called Router Model Binding. Solve situation ![](https://i.imgur.com/WZPpKc1.png) ### 404 page (without Router Model Binding) ```php= Route::get("/listings/{id}", function($id){ //pass value by URL $Liisting = Listing::find($id); if($Liisting){ return view("listing",[ 'listing' => $Liisting ]); } else{ abort(404); //throw httpException } }); ``` ![](https://i.imgur.com/YDPVx8R.png) ### Using Router model binding It could automatic check it ```php= Route::get("/listings/{listing}", function(Listing $listing){ //pass value by URL return view("listing",[ 'listing' => $listing ]); }); ``` ## Blade Component We could use component to deal with foreach items Use prop() the pass an array or values Invoke and pass data to commonents/listing-card.blade.php ```php= @foreach($listings as $listing) {{--Invoke and Pass value to commonents/listing-card.blade.php--}} <x-listing-card :listing="$listing" /> @endforeach ``` components/listing-card.blade.php ```php= @props(['listing']) {{--receive values (must be an array)--}} <html> contents ($listing->fieldName </html> ``` ### Use component to replace duplicate class or someting #### class replace ``` <div class="bg-gray-50 border border-gray-200 rounded p-6"> {{--duplicated style --}} {{$slot}} {{--items--}} </div> ``` ``` @props(['listing']) {{--receive values (must be an array)--}} <x-card> contents ($listing->fieldName </x-card> ``` ### Component Attributes if we write the code like this, we can additionally pad some tings on default values. card.bald.php ``` <div {{$attributes->merge(['class'=>"bg-gray-50 border border-gray-200 rounded p-6"])}}> {{--duplicated style --}} {{$slot}} {{--items--}} </div> ``` listing.blad.php ``` <x-card class="bg-pink-190"> contens </x-card> ``` (class ="bg-pink-190") is the additionally padding. ### tags component we muse use comma to separate the tags, turning that into an array. And then, use loop to display that. listing-card.bald.php ```php= <div class="text-xl font-bold mb-4">{{$listing->interests}}</div> <x-listing-tags :tagsCsv="$listing->expertiseSkill" /> <div class="text-lg mt-4"> ``` listing-tags.blad.php ```php @props(['tagsCsv']) {{--comma separated values--}} {{--separated string by the specified simble, and ture that into an array --}} @php $tags = explode(',', $tagsCsv) @endphp <ul class="flex"> @foreach ($tags as $tag) <li class="flex items-center justify-center bg-black text-white rounded-xl py-1 px-3 mr-2 text-xs" > <a href="/?tag={{$tag}}">{{$tag}}</a> </li> @endforeach </ul> ``` ## Controller We could use controller to return our view in web.php Create controller ``` php artisan make:controller ListingController ``` Replace function() with the controller, it could let it more cleaner and easily understand. ```php= //listings // Route::get('/', function () { // return view('listings', // [ // 'listings'=>Listing::all() // ] // ); //second args -> pass the value. // }); //Useing controller to replace function() Route::get('/',[ListingController::class,'index']); ``` ListingController.php ```php= class ListingController extends Controller { //Show the main page function index(){ return view('listings', [ 'listings'=>Listing::all() ]); } //Show the single page function show(Listing $listing){ return view("listing",[ 'listing' => $listing ]); } } ``` ## Resource Method Naming We could let every controller have one folder in the view folder and every function could be divided and created file put in the folder. ![](https://i.imgur.com/lqYkH0r.png) ![](https://i.imgur.com/C1evnP9.png) ## Using layout component Layout component could instead of @yield and @section components/layout.blade.php ``` <main> {{$slot}} </main> ``` Therefore, we could use <x-layout> to wrap the page. ## Tag Filter (scope filter) ### check request Method 1 :Using Dependency Injection ``` function(Request $request) dd($request) ``` Method 2 :Using helper ``` dd(request()) dd(request()->tag) == dd(request('tag')) ``` --- all() == latest()->get() But the latter was sorted by the latest to get. ListingsControler.php ```php= 'listings'=>Listing::latest()->get() ``` Since, we create the scope filter What i should able to do is add on to it ``` 'listings'=>Listing::latest()->filter(request(['tag']))->get() ``` filter(request(['tag'])'s values will pass to the model function array $filters ``` public function scopeFilter($query, array $filters){ dd($filters['tag']); } ``` the statement is mean if $filters['tag'] is not false then move on ```php if($filters['tag'] ?? false){ } ``` double question ?? (coalescing operator) Essentially, we are going here is sql like query ```php= if($filters['tag'] ?? false){ $query->where('expertiseSkill', 'like', '%'. request('tag') . "%"); } ``` ![](https://i.imgur.com/0x99oWS.png) ## Search Filter controller/ListingController ``` function index(){ return view('listings.index', [ // all() == latest()->get() but the latter was sorted by the latest to get 'listings'=>Listing::latest()->filter(request(['tag','search']))->get() ]); } ``` Models/Linsting.php ```php if($filters['search'] ?? false){ $query->where('name', 'like', '%'. request('search'). "%") ->orWhere('expertiseSkill', 'like', '%'. request('search'). "%") ->orWhere('interests', 'like', '%'. request('search'). "%") ->orWhere('description', 'like', '%'. request('search'). "%") ->orWhere('location', 'like', '%'. request('search'). "%"); //We could use orWhere to concate condictions. } ``` ## Clock Work package ``` composer require itsgoingd/clockwork ``` Attention !! Before we look information from the clockwork, we have to install clockwork extension packet. ![](https://i.imgur.com/ZA6RuGC.png) ## Create listings form Create a new route, new controller method and a new view . Route ```php= //Add new members Route::get('/listings/create',[ListingController::class,'create']); ``` Notice :this route position must in front of the /listings/{lisitngs} Otherwise , Route is going to be filtered by the router filter. controller ```php= // Add new personal file public function create(){ return view('listings.create'); } ``` Using component to wrap it view ``` <x-layout> <x-card class ="<except>"> constents <x-card> </x-layout> ``` ![](https://i.imgur.com/6x1crAa.png) ## Store and validation listings In Laravel , you have a post form you want to use directives and it's going to be at @csrf it's prevent cross-site-script attacks. ### Validation validate() is going to take in array we can specify what rules we want for certain fields if you have more then one rule then we can use a array to deal it . ```php $formFileds = $request->validate( [ "name"=> ["required", Rule::unique('listings', 'name')], "expertiseSkill"=> "required", "interests"=> "required", "location"=> "required", "email"=> ["required","email"], "website"=> "nullabe", "description"=> "required", ] ); ``` ### [Optional Fields](https://laravel.com/docs/9.x/validation#a-note-on-optional-fields) ``` website=>"nullable", ``` **Notice** In the migration, we have to set the file attribute is a nullable Just like this: ```php $table->string("website")->nullable(); ``` --- If any these fail when we submit the form, it'll actually send back error an error to the view Now to show the error, we can just go into our create view Under the input and use an error directive Show the error message in the create view ``` @error('name') <p class="text-red-500 text-xs mt-1">{{$message}}</p> @enderror ``` Save validated data into the database ```php= Listing::create($formFileds); ``` Mass Assignment Rule Now by default we have some protection where in order to add some fileds. Allow properties to mass assignment So, If you are able to have form to submit, you have to add protected $fillable. ```php protected $fillable = ['name' , "expertiseSkill", "interests", "location", "email", "description"]; ``` Other way to solve it appServerProvider.php If we do this, we're allow mass assignment no long to require adding $fillable to the Model. ```php public function boot() { Model::unguard(); //allow model to mass assigment } ``` Notice: you aware of what is going into the database if you are going to use that on guard ## Flash Message If we want to the message to tell us (something is success ), we could use them. it just store a memory for the one page load However, we have the couple way to implement the flash message. ### Method 1: Session ```php= Session::flash("message", "Create successfully > <"); ``` ### Method 2: Directly on the redirect Use with to pass the message Now, we need somewhere in our view to actually show it view/component ``` @if (session()->has('message')) <div class="fixed top-0 left-1/4 transform-translate-x-1/2 bg-laravel text-white px-48 py-3"> <p> {{session("message")}} </p> </div> @endif ``` We're go to pad it to the layout ``` <x-flashMessage /> ``` ## [alpine.js](https://alpinejs.dev/) alpine.js which is a light framework go will with our blade. if you want to click a button and have something pop up somewhere and just have some interactive functionality, and you can use alpine.js ### import alpine.js ``` <script src="//unpkg.com/alpinejs" defer></script> ``` ### x-data Declare a new Alpine component and its data for a block of HTML ``` x-data="{show:true}" ``` ### x-init Run code when an element is initialized by Alpine ``` x-init="setTimeout(()=> show = false,5000)" ``` if we want message to disappear after certain amount of time ### x-show Toggle the visibility of an element ``` x-show="show" ``` ### Source code ``` @if (session()->has('message')) <div x-data="{show:true}" x-init="setTimeout(()=> show = false,5000)" x-show="show" class="fixed top-0 left-1/4 transform-translate-x-1/2 bg-laravel text-pink-200 px-48 py-3"> <p> {{session("message")}} </p> </div> @endif ``` ## Keep old data in the form We got an error like if i leave some of these blank. After we submit we get the error, data in page will go away. So, we don't want that, we want whatever we put in the fields to stay there ``` value="{{old("name")}}" ``` ## Pagination if you have many listings on the page, i guess we have to use pagination Now, Let go to the controller , instead of get() change it to paginate() controller/index() ```php 'listings'=>Listing::latest()->filter(request(['tag','search']))->paginate() ``` And then, we need a way to actually see the number to click on the page number down index.blade.php ``` <div class="mt-6 p-4"> {{$listings->links()}} </div> ``` ### Add CSS Template ``` php artisan vendor:publish ``` ## File Upload ``` <form method="post" action="/listings" enctype="multipart/form-data"> ``` controller.listings.php ```php dd(request('photo')); or dd($requets->file('photo')); ``` why use file function. I thought the reaseon is html type="file" Default location is app/storage ### Config/filesystems.php if you want the image to be public ```php 'default' => env('FILESYSTEM_DISK', 'public'), //seconded parameter is form an array of "disk" ``` ### Save image path in the database ```php $table->string("photo")->nullable(); ``` nullable() means that if it doesn't have image that's fine. Update a database ``` php artisan migrate:refresh ``` ## Create sim link ### sim link public/image <-------> storage/app/public/personalPhoto Run command ``` php artisan storage:link ``` ![](https://i.imgur.com/FoO53Kz.png) listing-card ``` src="{{$listing->photo ? asset('storage/'. $listing->photo) : asset('/images/no-image.png')}}" ``` ## Edit listings Create the edit button ``` <x-card class="mt-4 p-2 flex space-x-6"> <a href="/listings/{{$listing->id}}/edit"> <i class="fa-solid fa-pencil"></i> Edit !! </a> </x-card> ``` web.php ```php //Edit Edit form Route::get('listings/{listing}/edit',[ListingController::class,'edit']); ``` controller.php ```php //Show Edit form public function edit(Listing $listing){ // dd($listing); return view("listings.edit", ['listing' => $listing]); } ``` ### Update web.php ```php //Edit form to update Route::put("/listings/{listing}", [ListingController::class, 'update']); ``` controller.php ```php public function update(Request $request, Listing $listing){ $formFileds = $request->validate( [ "name"=> "required", "expertiseSkill"=> "required", "interests"=> "required", "location"=> "required", "email"=> ["required","email"], "website"=> "nullable", "description"=> "required", ] ); // Upload the personal image if($request->hasFile('photo')){ //Check the upload file $formFileds["photo"]=$request->file('photo')->store("personalPhoto","public"); } // use static method to update data $listing->update($formFileds); //Flash message // Method 1 // Session::flash("message", "Create successfully > <"); // Method 2 use with to pass the message return redirect("/")->with("message", "Update Successfuly > < "); //return back()->with("message","Update Successfuly > <"); } ``` Update() code is similar to the store() code Just only modify some small places. ## Delete Listings We have to a submit delete request. view ``` {{-- Upadate --}} <form method="post" action="/listings/{{$listing->id}}"> @csrf @method("DELETE") <button class="text-red-500"><i class="fa-solid fa-trash"></i>Delete /// ^ /// </button> </form> ``` ![](https://i.imgur.com/kh2FhdW.png) web.php ```php //Delete the listing Route::delete("/listings/{listing}", [ListingController::class, 'delete']); ``` Controler ```php //Delete the listing public function destroy(Listing $listing){ $listing->delete(); return redirect("/")->with("message", "Deleted Successfuly > < "); } ``` ## User Registration New a controller ``` php artisan make:controller UserController ``` web.php ```php //Show Register Form Route::get("/register", [UserController :: class, "register"]); ``` Controller ```php public function create(){ return view('user.register'); } ``` password_confirmation that called is the convention we need to use for our validation for confirm functionally ```htmlembedded= <div class="mb-6"> <label for="password_confirmation" class="inline-block text-lg mb-2" > Confirm Password </label> <input type="password" class="border border-gray-200 rounded p-2 w-full" name="password_confirmation" /> @error('password_confirmation') <p class="text-red-500 text-ex mt-1">{{message}}</p> @enderror </div> ``` ## post request to the database ### Create new user handler Validation ```php $formFields = $request->validate([ 'name' => ['required','min:3'], 'acount' => ['required', 'min:6'], 'email' => ['required', 'email', Rule::unique('users','email')], 'password'=>['required', 'confirmed', 'min:6'] ]); ``` confirmed will make sure that it matches another filed called whatever this is underscore confirmation (x_confirmation) ### To hash password why do this, because you never want to store a plain text password in you database (avoid sql injection ) ```php //Hash password $formFields['password'] = bcrypt($formFields['password']); ``` ## Recommend VSC extension >PHP Intelephense >Laravel Blade Snippets >PHP Namespace Resolver More easily import the class what you need