Laragigs Tutorial

NOTE: due to the limitations of the conent, I am unable to post all of my notes but I have attached a file to this post of all the notes: Scroll all the way down to this page to download: 20-p5032-laragigsnotes.txt

THIS IS A LARAVEL 9 TUTORIAL I AM FOLLOWING TO LEARN HOW LARAVEL 9 WORKS. THE APPLICATION IS GREATE BECAUSE YOU ARE POSTING JOBS AND USES LOGIN TO CREATE JOB POSTINGS.
 

MYyJ4PuL4pY

Laravel From Scratch 2022 | 4+ Hour Course
Git Hub Repo: https://github.com/bradtraversy/laragigs

These are my notes:

Development environment:
########################

1. open virtualbox > Docker-ubuntu-100G - USE THIS ONE

  • Linux Ubuntu 20.04.3 LTS
  • Docker version 20.10.2, build 2291f61
  • "php": "^8.0.2",
  • "laravel/framework": "^9.19",

install laravel on linux:
https://laravel.com/docs/9.x#getting-started-on-linux

open terminal
$ cd ~/Desktop/local-laravel/tutorial/laragigs
$ curl -s https://laravel.build/laragigs | bash
# THIS WILL TAKE A WHILE AND GET THIS MESSAGE:
Thank you! We hope you build something incredible. Dive in with: cd laragigs && ./vendor/bin/sail up


$ cd laragigs
# CREATE ALIAS FOR SAIL
$ alias sail='bash vendor/bin/sail'
# START SAILing
$ sail up -d

# TO STOP:
$ sail down

# Sail without alias:
$ ./vendor/bin/sail up -d


#################### TIPS #########################
Laravel Sail: Execute And Send Terminal Command To Docker Container : https://www.webune.com/forums/azpyxw.html

Now you can execute any sail command to the contairner from the host machine:
$ sail php --version

Docker Command To Access Bast Terminal Inside A Container
https://www.webune.com/forums/zmpywz.html


First, find the container id with the following command:
$ docker ps -a

Now that you have the container id, you can use the following command:
$ sudo docker exec -i -t [DOCKERID] /bin/bash

#################### END TIPS #########################


open browser http://localhost

https://youtu.be/MYyJ4PuL4pY?t=381

open a new tab in terminal:

open vs code:
$ code .

open terminal: control + `
$ touch MyNotes.txt

# INSTALL EXTENSIONS:
1. laravel blade snippets by Winni Lin
2. PHP NameSpace Resolver by mehedi Hassan // https://youtu.be/MYyJ4PuL4pY?t=1425
3. PHP Intelephense by Ben Mewborn

ROUTING: https://youtu.be/MYyJ4PuL4pY?t=830
/routes/web.php

// THIS ROUTE POINTS TO: /resources/views/welcome.blade.php

Route::get('/', function () {
    return view('welcome');
});

// SIMPLE HELLO WORLD:

Route::get('/hello', function () {
    return 'Hello World';
});


// with response() HELLO WORLD:

Route::get('/hello', function () {
    return return response('Hello World');
});


// with 404 response() HELLO WORLD:

Route::get('/hello', function () {
    return response('Hello World',404);
});


// WITH HEADERS and CUSTOM HEADERS

Route::get('/hello', function () {
    return response('Hello World',200)
    // specify content type
    ->header('Content-Type', 'text-plain')
    // customer header
    ->header('foo', 'bar');
});

// PARAMS

// http://localhost/post/47
Route::get('/post/{post_id}', function ($post_id) {
    return response('Post Id:' . $post_id);
    // add contraints to only accept numbers. http://localhost/post/dhs WILL GIVE 404 ERROR
})->where('post_id','[0-9]+');


# DEBUGINGG: Dump, Die,

// http://localhost/post/47
Route::get('/post/{post_id}', function ($post_id) {
    dd($post_id); // Die and Dump. OUTPUT: "47" // routes/web.php:29
    return response('Post Id:' . $post_id);
    // add contraints to only accept numbers. http://localhost/post/dhs WILL GIVE 404 ERROR
})->where('post_id','[0-9]+');

# DEBUGINGG:  Dump, Die, Debug

// http://localhost/post/47
Route::get('/post/{post_id}', function ($post_id) {
    ddd($post_id); // Die and Dump
    return response('Post Id:' . $post_id);
    // add contraints to only accept numbers. http://localhost/post/dhs WILL GIVE 404 ERROR
})->where('post_id','[0-9]+');

//http://localhost/search?name=edwin&city=sfo

Route::get('/search', function (Request $request) {
    //dd($request); // show all
    dd($request->name); // show only the params OUTPUT: "edwin" // routes/web.php:38
});


## JSON API
// http://localhost/api/posts

Route::get('/posts', function () {
    //return JSON
    return response() ->json([
        'posts' => [
            'title' => 'Posts One'
        ]
    ]);
});

BLADE TEMPLATES:
open /resources/views/welcome.blade.php and remove the default HTML to:
<h1>Listings</h1>
open browser to http://localhost
TIP: you can rename and will work the same
FROM: /resources/views/welcome.blade.php
TO: /resources/views/listings.php

PASSING DATA
http://localhost
>>>>>>>>>>>>> routes/web.php

Route::get('/', function () {
    return view('listings',[
        'heading' => "Latest Listings",
        'listings' => [
        [
            'id' => 1,
            'title' => 'Listing One',
            'description' => 'This is the one description'
        ],
        'listings' => [
            'id' => 1,
            'title' => 'Listing Two',
            'description' => 'This is the two description'
        ]
    ],

    ]);
});

>>>>>>>>>>> views/listings.php
<h1><?php echo $heading; ?></h1>
<?php foreach($listings as $listing): ?>
<h2><?php echo $listing['title'] ?></h2>
<p><?php echo $listing['description'] ?></p>
<?php endforeach; ?>

#CHANGE TO A BLADE TEMPLATE:
rename
FROM: views/listings.php
TO: views/listings.blade.php

CLEAN IT UP:
<h1>{{$heading}}</h1>
@foreach($listings as $listing)
<h2>{{$listing['title']}}</h2>
<p>{{$listing['description']}}</p>
@endforeach

//https://youtu.be/MYyJ4PuL4pY?t=2038
REFRESH BROWSER: http://localhost and you will get same results

# EMBED PHP INTO BLADE TEMPLATE:
<h1>{{$heading}}</h1>
// php DIRECTIVE
@php
$foo = 1;
@endphp
{{$foo}}
@foreach($listings as $listing)
<h2>{{$listing['title']}}</h2>
<p>{{$listing['description']}}</p>
@endforeach

CONDITIONALS: in BLADE TEMPLATES
<h1>{{$heading}}</h1>
@if(count($listings == 0))
<p>No Listings Found</p>
@endif

// USING UNLESS
<h1>{{$heading}}</h1>
@unless( count($listings)==0 )
    @foreach($listings as $listing)
    <h2>{{$listing['title']}}</h2>
    <p>{{$listing['description']}}</p>
    @endforeach
@else
<p>No listings found</p>
@endunless

# MODELS https://youtu.be/MYyJ4PuL4pY?t=2202
# eloquent

manually create a model
$ touch app/Models/Listins.php
$ code app/Models/Listing.php

listings.php:

<?php

namespace App\Models;

class Listing
{
    public static function all(){
        return [
                [
                    'id' => 1,
                    'title' => 'Listing One',
                    'description' => 'This is the one description'
                ],
                [
                    'id' => 2,
                    'title' => 'Listing Two',
                    'description' => 'This is the two description'
                ]
        ];
    }
}


web.php:
ADD:
use App\Models\Listing;

Route::get('/', function () {
    return view('listings',[
        'heading' => "Latest Listings",
        'listings' => Listing::all()

    ]);
});


REFRESH BROWSER: http://localhost and you will get same results

LEFT OFF: https://youtu.be/MYyJ4PuL4pY?t=2378

create each $id page for each listings

web.php ADD:

// Single Listing
Route::get('/listings/{id}', function ($id) {
    return view('listing',[
        'listing' => Listing::find($id)

    ]);
});


ADD: Listings.php

    public static function find($id){
      $listings = self::all();
      foreach ($listings as $listing ) {
        if($listing['id']==$id){
            return $listing;
        }
      }
    }

CHANGE listings.blade.php:
FROM:
<h2>{{$listing['title']}}</h2>
TO:
<h2><a href="/listings/{{$listing['id']}}">{{$listing['title']}}</a></h2>

CREATE new file: listing.blade.php and ADD this code:
<h2>{{$listing['title']}}</h2>
<p>{{$listing['description']}}</p>

REFRESH BROWSER: http://localhost
CLICK ON EACH LINK:

MYSQL DATABASE:
left off: https://youtu.be/MYyJ4PuL4pY?t=2711

Laravel sail: login as root or create an root user
$ sail root-shell

# STOP sail to make db configuration changes:
$ sail down

DB Configurations at: /config/database.php

SET THE DEFAULT DATABASE:
1.  'default' => env('DB_CONNECTION', 'mysql'),
2. go to .env and set the variables:
DB_CONNECTION=mysql
DB_HOST=mysql
DB_PORT=3306
DB_DATABASE=[CHANGE THIS TO sqlite if needed]
DB_USERNAME=root
DB_PASSWORD=


EXAMPLE, to change to sqlite:
a. Change to: 'default' => env('DB_CONNECTION', 'sqlite'),
b. create a file in /database/database.sqlite

Add phpmyadmin
open docker-compose.yml file and add the following code after mailhog to install phpmyadmin in the docker container using port 8081 for the local and remote (ie Heroku) port

$ code ./docker-compose.yml

    phpmyadmin:
        image: phpmyadmin/phpmyadmin
        restart: always
        depends_on:
            - mysql
        ports:
            - "8081:80"
        environment:
            PMA_HOST: mysql
            MYSQL_ROOT_PASSWORD: '${DB_PASSWORD}'
        networks:
            - sail

# MORE INFO ABOUT PHPMYADMIN INTO DOCKER WITH LARAVEL:
https://www.webune.com/forums/api7.html


#REBUILD laravel in docker because we made changes to .env file:
$ sail down -v

# rebuild option 1, FASTER
$ sail build

# option 2 - THIS WILL TAKE LONGER AND DOWNLOADS THE FILES AGAIN
$ sail build --no-cache


$ sail up -d

PHPMYADIN:
Open your browswer to: http://localhost:8081/index.php
user: sail
password: password

http://localhost/listings/2

https://youtu.be/MYyJ4PuL4pY?t=2777
# ACCESS THE MYSQL DATABASE IN THE DOCKER CONTAINER:

https://gist.github.com/jjsquady/e12ce89c9029d520ce08f60a9ff8f304
Run this command in sail project folder:

$ docker-compose exec mysql bash

# mysql -u root -p

provide the default password: password


> SELECT User, Host FROM mysql.user;

# CREATE A NEW USER:
> CREATE USER 'edwin'@'localhost' IDENTIFIED BY 'password';
> GRANT ALL PRIVILEGES ON * . * TO 'edwin'@'localhost';
> FLUSH PRIVILEGES;

# validate newly created user:
> SELECT User, Host FROM mysql.user;

> exit

# login to mysql with new user:
$ mysql -u edwin -p

# create a new database, you should already have this database created in mysql, Rund this command to show the databases:
> SHOW DATABASES;

+--------------------+
| Database           |
+--------------------+
| information_schema |
| laragigs           |
| mysql              |
| performance_schema |
| sys                |
| testing            |
+--------------------+

If not, go ahead and create it now.
> CREATE DATABASE laragigs;

UPDATE .env file with your new user and credentials:
DB_DATABASE=laragigs
DB_USERNAME=edwin
DB_PASSWORD=password

# EXIT MYSQL
> exit
# exit

#LARAVEL MIGRATIONS
open /dtabase/migrations/2014_10_12_000000_create_users_table.php
$ code /dtabase/migrations/2014_10_12_000000_create_users_table.php

UP = will create users table
DOWN = drop users tables

### MIGRATIONS
Create the database migration model in the host, not the docker container, so be sure to have PHP and artisan already installed in the host machine.
for this example, I am using a windows 10 PC with virtuabox and Ubuntu with docker.
Machine Name: Docker-ubuntu-100G - USE THIS ONE

$ sail artisan make:migration create_listings_table
############################## ERROR:
Composer detected issues in your platform:

Your Composer dependencies require a PHP version ">= 8.1.0". You are running 7.4.3.

PHP Fatal error:  Composer detected issues in your platform: Your Composer dependencies require a PHP version ">= 8.1.0". You are running 7.4.3. in /home/developer/Desktop/local-laravel/tutorial/laragigs/laragigs/vendor/composer/platform_check.php on line 24

####################### SOLUTION: UPGRADE PHP 7 to 8 in UBUNTU LINUX - as of 12/15/2022, Laravel 9 runs on PHP 8

$ php -v
PHP 7.4.3 (cli) (built: Jun 13 2022 13:43:30) ( NTS )
Copyright (c) The PHP Group
Zend Engine v3.4.0, Copyright (c) Zend Technologies with Zend OPcache v7.4.3, Copyright (c), by Zend Technologies

NOTE: If you are running PHP 7.x, you can continue with this guide to upgrade to PHP 8.
FOLLOW THIS GOOD TUTORIAL: https://www.cloudbooklet.com/how-to-upgrade-php-version-to-php-8-0-on-ubuntu/

$ sudo apt install software-properties-common
ERROR: dpkg was interrupted, you must manually run 'sudo dpkg --configure -a' to correct the problem.
$ sudo dpkg --configure -a
$ sudo apt install software-properties-common


$ sudo add-apt-repository ppa:ondrej/php
$ sudo apt update
# THE MOST STABLE VERSION CURRENTLY
$ sudo apt install php8.2
# INSTALL EXTENSIONS
sudo apt install php8.0-common php8.0-mysql php8.0-xml php8.0-xmlrpc php8.0-curl php8.0-gd php8.0-imagick php8.0-cli php8.0-dev php8.0-imap php8.0-mbstring php8.0-opcache php8.0-soap php8.0-zip php8.0-intl -y


$ php -v
PHP 8.2.0 (cli) (built: Dec 10 2022 10:52:42) (NTS)
Copyright (c) The PHP Group
Zend Engine v4.2.0, Copyright (c) Zend Technologies
    with Zend OPcache v8.2.0, Copyright (c), by Zend Technologies
####################### END

lets try again:

$ sail artisan make:migration create_listings_table

ANOTHER ERROR:

  Class "DOMDocument" not found

  at vendor/nunomaduro/termwind/src/HtmlRenderer.php:32
     28▕      * Parses the given html.
     29▕      */
     30▕     public function parse(string $html): Components\Element
     31▕     {
  ➜  32▕         $dom = new DOMDocument();
     33▕
     34▕         if (strip_tags($html) === $html) {
     35▕             return Termwind::span($html);
     36▕         }

      +21 vendor frames
  22  artisan:35
      Illuminate\Foundation\Console\Kernel::handle()

SOLUTION #1:
SOURCE: https://stackoverflow.com/a/14395414
$ sudo apt-get install php-dom

ERROR:
Reading package lists... Done
Building dependency tree
Reading state information... Done
Package php-dom is a virtual package provided by:
  php8.2-xml 8.2.0-3+ubuntu20.04.1+deb.sury.org+1
  php8.1-xml 8.1.13-1+ubuntu20.04.1+deb.sury.org+1
  php8.0-xml 1:8.0.26-1+ubuntu20.04.1+deb.sury.org+1
  php7.4-xml 1:7.4.33-1+ubuntu20.04.1+deb.sury.org+1
  php7.3-xml 7.3.33-8+ubuntu20.04.1+deb.sury.org+1
  php7.2-xml 7.2.34-36+ubuntu20.04.1+deb.sury.org+1
  php7.1-xml 7.1.33-50+ubuntu20.04.1+deb.sury.org+2
  php7.0-xml 7.0.33-63+ubuntu20.04.1+deb.sury.org+2
  php5.6-xml 5.6.40-63+ubuntu20.04.1+deb.sury.org+2
You should explicitly select one to install.

E: Package 'php-dom' has no installation candidate

SOLUTION #2 -
$ sudo apt-get install php8.2-xml


Try again:
$ sail artisan make:migration create_listings_table
SUCCESS!!! FINALLY!!!!!
INFO  Migration [2022_12_17_050042_create_listings_table] created successfully.

$ code database/migrations/2022_12_17_050042_create_listings_table.php

https://youtu.be/MYyJ4PuL4pY?t=3053

Add the following after id:

        Schema::create('listings', function (Blueprint $table) {
            $table->id();
            $table->string('title');
            $table->string('tags');
            $table->string('company');
            $table->string('location');
            $table->string('email');
            $table->longText('description');
            $table->string('website');
            $table->timestamps();
        });

RUN THE NEWLY CREATED MIGRATION IN THE DOCKER CONTAINER WITH SAIL

$ sail artisan migrate

ERROR: SQLSTATE[HY000] [1045] Access denied for user 'edwin'@'172.18.0.7' (using password: YES) (SQL: select * from information_schema.tables where table_schema = laragigs and table_name = migrations and table_type = 'BASE TABLE')

SOLUTION: restart sail because we need to update the user MYSQL password from the .env file
$ sail down
$ sail build
$ sail up -d

LETS TRY AGAIN:
$ sail artisan migrate

GOT ERROR AGAIN :
DB_CONNECTION=mysql
DB_HOST=mysql
DB_PORT=3306
DB_DATABASE=laragigs
DB_USERNAME=sail
DB_PASSWORD=password

SOLUTION:
$ sail down
& make sure you have these in .env file:

DB_CONNECTION=mysql
DB_HOST=mysql
DB_PORT=3306
DB_DATABASE=laragigs
DB_USERNAME=sail
DB_PASSWORD=password

# THIS TIME IT WORKED!!
$ sail up -d
$ sail artisan migrate

   INFO  Preparing database.

  Creating migration table ............................................................................................ 83ms DONE

   INFO  Running migrations.

  2014_10_12_000000_create_users_table ............................................................................... 330ms DONE
  2014_10_12_100000_create_password_resets_table ...................................................................... 69ms DONE
  2019_08_19_000000_create_failed_jobs_table ......................................................................... 130ms DONE
  2019_12_14_000001_create_personal_access_tokens_table ............................................................... 84ms DONE
  2022_12_17_050042_create_listings_table ............................................................................. 28ms DONE

# OPEN PHP MYADMIN TO CHECK THE NEW TABLE WAS CREATED:
http://localhost:8081/tbl_structure.php?db=laragigs&table=listings

OR USE MYSQL COMMANDS:
$ docker-compose exec mysql bash
# mysql -u sail -p

# create a new database, you should already have this database created in mysql, Rund this command to show the databases:
> use laragigs;

> SHOW TABLES;
+------------------------+
| Tables_in_laragigs     |
+------------------------+
| failed_jobs            |
| listings               |
| migrations             |
| password_resets        |
| personal_access_tokens |
| users                  |
+------------------------+
6 rows in set (0.00 sec)

>exit
# exit



# SEEDINGS: OPEN /database/seeders/DatabaseSeeder.php
$ code database/seeders/DatabaseSeeder.php

LOOK AT DatabaseSeeder.php YOU CAN SEE A LINE:
THIS LINE CREATE 10 DUMMY USERS FROM THE FILE: /database/factories/UserFactory.php
UNCOMMENT:
// \App\Models\User::factory(10)->create();

$ sail artisan db:seed

CHECK PHPMYADMIN YOU WILL SEE 10 NEW FAKE USERS CREATED

another option to phpmyadmin is dbeaver or mysql workbench

you got undo the 10 fake users simply by commenting the users factory and run the migrate command:
// \App\Models\User::factory(10)->create();

$ sail artisan migrate:refresh

ucomment: // \App\Models\User::factory(10)->create();

NOTE: you can refresh and seed:
$ sail artisan migrate:refresh --seed

CHECK the users table and all 10 fake users should be gone :)

LETS SEED SOME LISTSINGS
RENAME/DELETE THE Listing.php  MODEL WE CREATED
$ mv app/Models/Listing.php app/Models/ListingV1.php

in the host machine to create a Listing.php model in app/Models/Listing.php
$ sail artisan make:model Listing
$ code app/Models/Listing.php

https://youtu.be/MYyJ4PuL4pY?t=3544



try http://localhost/ (you wont get errrors)
Why no errors: because, if you look in web.php the all() and find() are alonquent methods.

// ADD HARD CODED SEEDERS DATA
open DatabaseSeeder.php and add the following after \App\Models\User::factory(10)->create();

        Listing::create(
            [
                'title' => 'Laravel Senior Developer',
                'tags' => 'laravel, javascript',
                'company' => 'Acme Corp',
                'location' => 'Boston, MA',
                'email' => '[email protected]',
                '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?'
            ]
        );

        Listing::create(
            [
                'title' => 'Full-Stack Engineer',
                'tags' => 'laravel, backend ,api',
                'company' => 'Stark Industries',
                'location' => 'New York, NY',
                'email' => '[email protected]',
                'website' => 'https://www.starkindustries.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?'
            ]
        );


generate the seeder:

$ sail artisan migrate:refresh --seed

CHECK PHPMYADMIN FOR THE NEW LISTSINGS:
http://localhost:8081/sql.php?db=laragigs&table=listings&pos=0

CHECK THE WEBPAGE AND YOU WILL SEE THE TWO NEW LISTINGS:
http://localhost/
http://localhost/listings/1

NOW, LETS CREATE SOME FAKE LISTINGS WITH FACTORY;

$ sail artisan make:factory ListingFactory
$ code database/factories/ListingFactory.php

change TO:

    public function definition()
    {
        return [
            'title' => $this->faker->sentence(),
            'tags' => 'laravel, api, backend',
            'company' => $this->faker->company(),
            'email' => $this->faker->companyEmail(),
            'website' => $this->faker->url(),
            'location' => $this->faker->city(),
            'description' => $this->faker->paragraph(5),
        ];
    }

$ code database/seeders/DatabaseSeeder.php
COMMENT the two Listing::create(
ADD after \App\Models\User::factory(10)->create();
Listing::factory(6)->create();

NOW YOU ARE READY TO MIGRATE

$ sail artisan migrate:refresh --seed

CHECK THE WEBPAGE AND YOU WILL SEE THE TWO NEW LISTINGS:
http://localhost/
http://localhost/listings/1


SAVE TO GIT
git init
code .gitignore
git add .
git commit -m "WORKS: Basic with MyQSL Output to HTML finished at: https://youtu.be/MYyJ4PuL4pY?t=4045"
COMMIT: 4fd8c16



L A Y O U T S
https://youtu.be/MYyJ4PuL4pY?t=4045

views> layout.blade.php
$ touch resources/views/layout.blade.php
$ code resources/views/layout.blade.php
add HTML 5 template

https://youtu.be/MYyJ4PuL4pY?t=4354
$ mkdir public/images
// PLACE THE FOLLOWING IMAGES FROM THE TEMPLATE IN THIS FOLDER:
https://github.com/bradtraversy/laragigs/tree/main/_laragigs_theme/images
laravel-logo.png
no-image.png
logo.png

wget https://raw.githubusercontent.com/bradtraversy/laragigs/main/_laragigs_theme/images/logo.png -P ./public/images/
wget https://raw.githubusercontent.com/bradtraversy/laragigs/main/_laragigs_theme/images/no-image.png -P ./public/images/
wget https://raw.githubusercontent.com/bradtraversy/laragigs/main/_laragigs_theme/images/laravel-logo.png -P ./public/images/



with layout, in the blade template, you can two ways to enacapsulation:
1. {{$listing['title']}}
2. {{$listing->title}} // Using eloquent collection

images asset helper for public images:
FROM: src="path/imag.jpg"
TO: src="{{asset('path/image.jpg')}}"

PARTIALS: https://youtu.be/MYyJ4PuL4pY?t=4778
$ mkdir resources/views/partials
$ touch resources/views/partials/_hero.blade.php
$ code resources/views/partials/_hero.blade.php

$ touch resources/views/partials/_search.blade.php
$ code resources/views/partials/_search.blade.php

// TO INCLUDE ABOVE PARTIALS
@include('partials._hero')
@include('partials._search')


ROUTE MODEL BINDING:
https://youtu.be/MYyJ4PuL4pY?t=5162
EXAMPLE: if you go to url: http://localhost/listings/NotExists - YOU GET AN ERROR:
SOLUTION: SHOW A 404 PAGE
OPEN web.php

###########################################
WITHOUT ROUTE MODEL BINDING:
###########################################
CHANGE FROM:

Route::get('/listings/{id}', function ($id) {
    return view('listing',[
        'listing' => Listing::find($id)

    ]);
});

CHANGE TO:

Route::get('/listings/{id}', function ($id) {
    $listing = Listing::find($id);

    if ($listing) {
        return view('listing', [
            'listing' => $listing

        ]);
    } else {
        abort('404');
    }
});


NOW WHEN YOU GOTO http://localhost/listings/NotExists, YOU WILL GET A 404 ERROR
###########################################
WITH ROUTE MODEL BINDING:
###########################################
Route::get('/listings/{listing}', function (Listing $listing) {
        return view('listing', [
            'listing' => $listing
        ]);
});
NOW WHEN YOU GOTO http://localhost/listings/NotExists, YOU WILL GET A 404 ERROR




git commit -am "WORKS: Has styling using LAYOUTS: NEXT WILL USE COMPONENTS: https://youtu.be/MYyJ4PuL4pY?t=5298"
af4c106

COMPONENTS WITH PROPS
https://youtu.be/MYyJ4PuL4pY?t=5298

# CREATE A COMPONENTS FOLDER:
$ mkdir resources/views/components
$ touch resources/views/components/listing-card.blade.php
$ code resources/views/components/listing-card.blade.php

to consume the compent you will add in the blade template:
@foreach($listings as $listing)
    <x-listing-card :listing="$listing"/>
@endforeach

https://youtu.be/MYyJ4PuL4pY?t=5447
# CREATE A CARD COMPONET TO WRAP CONTENT TO:
$ touch resources/views/components/card.blade.php
$ code resources/views/components/card.blade.php

{{$slot}}
<x-card> </x-card>

https://youtu.be/MYyJ4PuL4pY?t=5662
PASS ATTRIBUTTONES TO SLOTS
CHANGE FROM:
<div class="bg-gray-50 border border-gray-200 rounded p-6">
{{$slot}}
</div>

TO:
<div {{$attributes->merge(['class'=>'bg-gray-50 border border-gray-200 rounded p-6'])}} >
{{$slot}}
</div>

USAGE: <x-card class="p-14">
THIS WILL PASS A CLASS OF 9-14 TO THIS DIV

git commit -am "WORKS: USING components and props: NEXT WILL USE CONTROLLERS: https://youtu.be/MYyJ4PuL4pY?t=5720"
master e341ed6]

HOW TO ADD A NEW COMPONENT IN LARAVEL: EXAMPLEl TAGS:

1. CREATE A NEW FILE CALLED: listing-tags.blade.php
$ touch resources/views/components/listing-tags.blade.php
$ code resources/views/components/listing-tags.blade.php

2. ADD THE DESIRED PROPS AND CODE TO listing-tags.blade.php
@props(['tagsCvs']) // receiving an array from the props, This is a value from the database
HTML.....
if you have an array, you can use a foreach loop:
@foreach ($tags as $tag)

 @endforeach

3. Use the newly created component in a blade template. Example: listing-card.blade.php
<x-listing-tags :tagsCsv="$listing->tags" />
NOTE: "listing-tags" refers to file: resources/views/components/listing-tags.blade.php
PASSING PROP tagsCsv is the name of the variabl listing-tags.blade.php can use and its value.

DONE:

CONTROLLERS: https://youtu.be/MYyJ4PuL4pY?t=6033

CREATE A CONTROLLER:
$ sail artisan make:controller ListingController

RESULTS: a controller will be created in: app/Http/Controllers/ListingController.php

OPEN NEW CONTROLLER: https://youtu.be/MYyJ4PuL4pY?t=6107
$ code app/Http/Controllers/ListingController.php

1. ListingController.php - create a method for each get
     // show all listings
    public function index() {
    }
    public function show() {

    }

2. web.php cut the following code:
Route::get('/', function () { // DON"T CUT THIS LINE
    return view('listings', [
        'listings' => Listing::all() // getting data by using the App\Models\Listing; and the all() moethod

    ]);
});// DON"T CUT THIS LINE

3. ListingController.php index method should look like this:


// IMPORT CLASSES:
use App\Models\Listing;
use Illuminate\Http\Request;
use App\Http\Controllers\ListingController;

// UPDATE THE INDEX AND SHOW METHODS
class ListingController extends Controller {
    // show all listings
    public function index() {
        return view('listings', [
            'listings' => Listing::all() // getting data by using the App\Models\Listing; and the all() moethod

        ]);
    }
    // show single listing
    public function show(Listing $listing) {
        return view('listing', [
            'listing' => $listing
        ]);
    }
}


4. web.php remove the functions and add the contorllers
CHANGE FROM:
Route::get('/', function () {

});
Route::get('/listings/{listing}', function (Listing $listing) {

});

CHANGE TO:
Route::get('/', [ListingController::class, 'index']);
Route::get('/listings/{listing}', [ListingController::class, 'show']);

DID NOT WORK:
ERROR: Target class [ListingController] does not exist.
SOLUTION: NEEDED TO ADD use App\Http\Controllers\ListingController; in web.php


git commit -am "WORKS: Added 'index' and 'show' CONTROLLERS. finished at: https://youtu.be/MYyJ4PuL4pY?t=6238"
COMMIT: 0422b8c

###########################################################################################
// CONTROLLERS NAMING CONVETIONS:
// 7 Common Resource Routes:
// index - Show all listings
// show - SHOW single listing
// create - Show form to create new listing (POST)
// edit - show form to edit listing
// update - update listing (POST)
// destroy - Delete Listing

TO MAKE THIS WORK IN OUR APPLICATION, WE WILL NEED TO CREATE A NEW FOLDER IN VIEWS: views/listings and move the two files inside:
mkdir resources/views/listings
mv resources/views/listing.blade.php resources/views/listings/show.blade.php
mv resources/views/listings.blade.php resources/views/listings/index.blade.php

developer@developer-Docker:~/Desktop/local-laravel/tutorial/laragigs/laragigs

$ ls resources/views/listings


index.blade.php  show.blade.php

ListingController.php
CHANGE FROM:

    public function index() {
        return view('listings', [
            'listings' => Listing::all() // getting data by using the App\Models\Listing; and the all() moethod

        ]);
    }
    // show single listing
    public function show(Listing $listing) {
        return view('listing', [
            'listing' => $listing
        ]);
    }

CHANGE TO:


    // show all listings
    public function index() {
        return view('listings.index', [
            'listings' => Listing::all() // getting data by using the App\Models\Listing; and the all() moethod

        ]);
    }
    // show single listing
    public function show(Listing $listing) {
        return view('listings.show', [
            'listing' => $listing
        ]);
    }

REFRESH BROWSER: http://localhost/listings/1

[OK]
###########################################################################################


LAYOUT OPTION:

https://youtu.be/MYyJ4PuL4pY?t=6392

LAYOUT option 1:
1. create a file in resources/views/layout.blade.php
2. in add @yield('content')  where the dynamic content will go from resources/views/listings/index.blade.php
3. in index.blade.php add @extends('layout') at the top of the file
4. in index.blade.php add wrap the content you wan to render for the layout with:
@section('content')
@endsection

LAYOUT option 2 (as a component)
1. mv resources/views/layout.blade.php resources/views/components/layout.blade.php
2. in layout.blade.php CHANGE:
FROM:  @yield('content')
TO: {{$slot}}
3. in index.blade.php, REMOVE:
    @extends('layout')
    @section('content')
        .... content
    @endsection
REPLACE WITH:
    <x-layout>
        .... content
    </x-layout>


git commit -am "WORKS: change LAYOUT TO COMPONENT TYPE. finished at: https://youtu.be/MYyJ4PuL4pY?t=6507"
COMMIT: ce4df60

###########################################################################################

F I L T E R I N G https://youtu.be/MYyJ4PuL4pY?t=6545
GET URL PARAMS: http://localhost/?tag=laravel

open web.php
CHANGE: FROM:
    public function index() {
        return view('listings.index', [
            'listings' => Listing::all()

        ]);
    }
CHANGE TO:
    public function index(Request $request) {
        dd($request->query);
        return view('listings.index', [
            'listings' => Listing::all() // getting data by using the App\Models\Listing; and the all() moethod

        ]);
    }
OPTION 2 CHANGE TO:
    public function index() {
        dd(request('tag')); // OUTPUTS 'laravel'
        //or
        // dd(request()->tag); // OUTPUTS 'laravel' https://youtu.be/MYyJ4PuL4pY?t=6693
        return view('listings.index', [
            'listings' => Listing::all() // getting data by using the App\Models\Listing; and the all() moethod

        ]);
    }

SCOPE FILTER: https://youtu.be/MYyJ4PuL4pY?t=6710

code app/Models/Listing.php
ADD after HasFactory
        public function scopeFilter($query, array $filters) {
            //dd($filters['tag']); //OUTPUT 'laravel' from http://localhost/?tag=laravel
            // check if tag is FOUND
            if($filters['tag'] ?? false){
                // database query
                $query->where('tags', 'like', '%' . request('tag') . '%');
            }
        }
}
in file app/Http/controllers/ListingController.php
CHANGE FROM: 'listings' => Listing::all()
// sort by the latest and filter whatever the tag PARAM value is
TO:  'listings' => Listing::latest()->filter(request(['tag']))->get()

FILTER BY SEARCH
URL: http://localhost/?search=Incidunt

in file app/Http/controllers/ListingController.php
CHANGE: FROM:
'listings' => Listing::latest()->filter(request(['tag']))->get()
TO: 'listings' => Listing::latest()->filter(request(['tag','search']))->get()

in file app/Models/Listing.php
ADD in scopeFilter()
            if($filters['search'] ?? false){
                // database query
                $query->where('title', 'like', '%' . request('search') . '%')
                ->orWhere('description', 'like', '%' . request('search') . '%')
                ->orWhere('tags', 'like', '%' . request('search') . '%');

            }

Try the search bar, it should work:
Example: http://localhost/?search=Architecto
1 result



git commit -am "WORKS: Added filtering for tags and search bar keyword. finished at: https://youtu.be/MYyJ4PuL4pY?t=7253"
COMMIT: 995ec35

###########################################################################################


https://youtu.be/MYyJ4PuL4pY?t=7289

CLOCK WORK

https://chrome.google.com/webstore/detail/clockwork/dmggabnehkmmfmdffgajcflpdjlnoemp?hl=en

composer require itsgoingd/clockwork
###########################################################################################
POST JOBS using the POST FORM
https://youtu.be/MYyJ4PuL4pY?t=7451

web.php add new route with create

ListingController.php add new create() method

create view create.blade.php and <x-layout>

ADD A NEW PAGE: Post new Listing




1. SET VARIABLES:
#########################
ADD STORE TO SAVE THE FORM DATA:
NEWPAGENAME='store'
PAGETYPE='post'
COMPONENTNAME='listings'

2. COPY AND PASTE COMMANDS TO TERMINAL:
######################################
echo "Route::$PAGETYPE('/$COMPONENTNAME/$NEWPAGENAME', [ListingController::class, '$NEWPAGENAME']);" >> routes/web.php
CONTROLERCODE="public function $NEWPAGENAME() {\n\t\t\treturn view('$COMPONENTNAME.$NEWPAGENAME');\n\t\t}"
sed -i "\/\/\list/a $CONTROLERCODE" app/Http/Controllers/ListingController.php
touch resources/views/listings/$NEWPAGENAME.blade.php
VIEWCODE=$'<x-layout>\n\t... content\n</x-layout>'
echo "$VIEWCODE" > resources/views/listings/$NEWPAGENAME.blade.php
code resources/views/listings/$NEWPAGENAME.blade.php
xdg-open http://localhost/$COMPONENTNAME/$NEWPAGENAME
echo done


SAME COMMANDS ABOVE BUT MORE DETAILS AND EXPLANATION
###################################
workflow to add a new functionality example: 'create'
###################################
$ NEWPAGENAME='create'
$ PAGETYPE='get'
$ COMPONENTNAME='listings'
$ echo NEWPAGENAME

###### 1. ADD ROUTE ######
1. Route - web.php
    // APEND A NEW ROUTE
    $ echo "Route::$PAGETYPE('/$COMPONENTNAME/$NEWPAGENAME', [ListingController::class, '$NEWPAGENAME']);" >> routes/web.php

###### 2. ADD CONTROLLER METHOD ######
2. In the Class Controller, add new method - App\Http\Controllers\ListingController.php
        // ListingController.php - create methods for FORM USING THISE COMMANDS:
        $ CONTROLERCODE="public function $NEWPAGENAME() {\n\t\t\treturn view('$COMPONENTNAME.$NEWPAGENAME');\n\t\t}"
        $ sed -i "\/\/\list/a $CONTROLERCODE" app/Http/Controllers/ListingController.php

        THE ABOVE TWO COMMAND WILL THIS METHOD TO ListingController.php
        public function create() {
            return view('listings.create');
        }
###### 3. ADD VIEW ######
3. View, createa a new file :
    $ touch resources/views/listings/$NEWPAGENAME.blade.php
    $ VIEWCODE=$'<x-layout>\n\t... content\n</x-layout>'
    $ echo "$VIEWCODE" > resources/views/listings/$NEWPAGENAME.blade.php
    $ code resources/views/listings/$NEWPAGENAME.blade.php

    the above commands will add these HTML to Wrap your code with the Layout:
    <x-layout>
        ... content
    </x-layout>
###### 4. CONFIRM NEW PAGE ######
4. View the new component: http://localhost/listings/create
IMPORTANT: if you get a '404 Not Found' - look at ListingController.php to make sure the routes are in order.
the system will dow down to each down the list and will render the first match whatever the url is.
EXAMPLE: URL: /listings/1 will match 'listings/create' and 'listings/1'
CHECK IT OUT: https://youtu.be/MYyJ4PuL4pY?t=7667

CREATE FORM TIPS:
* every input must have a name="" to setup the input variables
* must have action (example: action="/listings")
* SECURITY: for method="POST", in laravel you MUST use the @CSRF directive to prevent cross site scripting attacks - https://youtu.be/MYyJ4PuL4pY?t=7884


# FORM VALIDATION:
ListingController.php
        // VALIDATION: https://laravel.com/docs/9.x/validation
        $formFields = $request->validate([
            'title'=>'required'
        ]);


ERROR:
I TRIED TO IMPLEMENT JQUERY V1 OR V3 FOR THE CREATE FORM, USING A BUTTON WHEN ONCLICK TO RUN THIS CODE BUT THE FIELD WOULD NOT EMPTY ONLY THE CSSS?
$('#description').css('background-color','yellow').val('');
BUT WHEN I DO IN THE CONSOLE IT WORKS??


PHP SPREAD OPERATOR:
$arr1 = [1,2,3];
$arr2 = [4,5,6];
$arr3 = [1,2,3 ...$arr2];
ouput: [1,2,3,4,5,6]

ERROR after submit form: https://youtu.be/MYyJ4PuL4pY?t=8609
Illuminate \ Database \ Eloquent \ MassAssignmentException
Add [company] to fillable property to allow mass assignment on [App\Models\Listing].
SOLUTION:
#1. Add a $fillable array variable in app/Models/Listing.php just below use HasFactory;
#2. Another solution: open app/Providers/AppServiceProvider.php
ADD  Model::unguard(); in boot() function
IMPORT use Illuminate\Database\Eloquent\Model;
COMMENT OUT the array from option #1 above
TRY IT AGAIN AND IT SHOULD WORK:


git commit -am "WORKS: Added POST submit form and enter into database. finished at: https://youtu.be/MYyJ4PuL4pY?t=8840"
COMMIT: 6eab779

###########################################################################################
FLASH MESSAGE AFTER FORM SUBMITTED - this is a temporary message when the form has been submitted successcully

https://youtu.be/MYyJ4PuL4pY?t=8840"

1. firs way with session

open ListingController.php
in the store method:
CHANGE FROM: return redirect('/');
CHANGE TO:   return redirect('/')->with('message','Listing created successfuly!');

2. create a component callmed message:
touch resources/views/components/flash-message.blade.php
code resources/views/components/flash-message.blade.php

3. add Alpine.js <script src="//unpkg.com/alpinejs" defer></script>

4. Addthe following to the tag for the message to stay showing for 3 seconds long
    <div
    x-data="{show: true}"
    x-init="setTimeout(() => show = false, 3000)"
    x-show="show"


git commit -am "WORKS: added a flash message after form submit with alpine.js : https://youtu.be/MYyJ4PuL4pY?t=9392"
d4d4b7e
###########################################################################################
KEEP OLD DATE IN FORM. https://youtu.be/MYyJ4PuL4pY?t=9392
by default, if you submit the laravel form and there is an error, the fields will become blank. one way to this is to add the value="" attribute as the following:
<input value="{{old('NAME_OF_FIELD')}}"

git commit -am "WORKS: when form has errors, keep the old submitted data in the form : https://youtu.be/MYyJ4PuL4pY?t=9529"
5d31278
###########################################################################################

PAGINATION
1. open ListingController.php
2. in the index controller,
CHANGE FROM: get()
CHANGE TO: paginate()
3. ADD

        // SHOW THE OBJECT PROPERTY AVAILABLE, SUCH AS HOW MANY PAGES ARE AVAILABLE
        //simplePaginate() is also available
        dd(Listing::latest()->filter(request(['tag', 'search']))->paginate(2));
OUTPUT:
Illuminate\Pagination\LengthAwarePaginator {#1238 ▼ // app/Http/Controllers/ListingController.php:16
  #items: Illuminate\Database\Eloquent\Collection {#618 ▶}
  #perPage: 2
  #currentPage: 1
  #path: "http://localhost"
  #query: []
  #fragment: null
  #pageName: "page"
  +onEachSide: 3
  #options: array:2 [▶]
  #total: 12
  #lastPage: 6
}

4. ADD THE PAGES NUMBER NAVIGATION
in index.blade.php add the following for the NAVIGATION pagination bar:
    <div class="mt-6 p-4">
        {{$listings->links()}}
    </div>

5. To style the NAVIGATION PAGINATION bar, you will need to publish the project:
$ sail artisan vendor:publish

ERROR:  Call to undefined function Termwind\ValueObjects\mb_strimwidth()
SOLUTION: sudo apt-get install php-mbstring
SOURCE: https://laracasts.com/discuss/channels/laravel/call-to-undefined-function-mb-strimwidth

SELECT 4 : Provider: Illuminate\Pagination\PaginationServiceProvider

6. a new directory is created in resources/views/vendor you can look at the files in this directory to customer the tailwind classes

git commit -am "WORKS: Added Paginations Completed to: https://youtu.be/MYyJ4PuL4pY?t=9920"
84d5cfe
###########################################################################################

UPLOAD FILES IN FORM: https://youtu.be/MYyJ4PuL4pY?t=9922

1. open create.blade.php
$ code resources/views/listings/create.blade.php

create.blade.php should look like this: https://raw.githubusercontent.com/bradtraversy/laragigs/main/resources/views/listings/create.blade.php

2. add enctype to <form method="POST" action="/listings/store" enctype="multipart/form-data">

3. The form is submitted to ListingController.php, so open file
$ code app/Http/Controllers/ListingController.php
go to the store method and add the following to test out the REQUEST['file'] properties
dd($request->file('logo'));

4. submit the form and look for the following properties:
  -originalName: "logo.png"
  -mimeType: "image/png"
  -error: 0
  #hashName: null

5. by default, the uploaded files go to laragigs/storage/app/

6. Open configs/filesystem.php
$ code config/filesystems.php
look for the line for the default disk, example: local
'default' => env('FILESYSTEM_DISK', 'local'),
Scroll down and look for the disk => [] array for 'local' you will see the 'root' => storage_path('app'),
to change the uploads to the public folder, look for the 'public' property or AMAZON
CHANGE FROM: 'default' => env('FILESYSTEM_DISK', 'local')
CHANGE TO: 'default' => env('FILESYSTEM_DISK', 'public')

5. Add the new logo field to the database migrations:
a. open 2022_12_17_050042_create_listings_table.php
    $ code database/migrations/2022_12_17_050042_create_listings_table.php
b. ADD the following to the up() Schemma:
$table->string('logo')->nullable(); // nullable means i

20-p5032-addnewpage-bash-script.txt
20-p5032-laragigsnotes.txt