[ Prev ] [ Index ] [ Next ]

Laravel

Created Friday 08 March 2019


Ait, the new job requires it. Let's get started with this, apparently, awesome framework.


Cheatsheet: https://cheats.jesse-obrien.ca/


Installation

# You will be requiring laravel via composer, so make sure that is installed.
# Also make sure that composer is in your $PATH (.bashrc i.e.)

export PATH="$HOME/.config/composer/vendor/bin:$PATH"


# Then install laravel
$ composer global require "laravel/installer"


Usage

# There's quite a lot to do with laravel and its components


Create project

# Now that laravel is installed, you can use the laravel command
# Navigate to the folder, where you want the new project to be (www)
$ laravel new ProjectName


Artisan

# If you navigate to the newly create project ProjectName you can use a lil' extra "php" commands
# To get an overview - don't worry, you should learn with time.
$ php artisan


Server

# While in the project folder, start a deployment server
$ php artisan serve


# Now you can navigate to http://127.0.0.1:8000/ and see the default laravel page (new project)


Project files

# route > web.php
# Shows the URL paths/locations (sub pages) and what view they return
# Copy the already existing function, to create more sub pages (remember to change the / )


# resources > views
# The views that can be returned via route
# It seems like the names of the files (views) needs a .blade.php suffix (i.e. contact.blade.php )


Create a layout file

# Like in my own TinyMVC, where root folders index.php has all the "static" data for all pages
# The "layout"-ish page, which then loads all the content via that page.


# In the views folder, create a layout.blade.php file.
# Create the HTML typical document and place a @yield('') which in return will be called from the
# other documents. It'll indicate that this is where we want the content, from other views, to
# be displayed. Below is simplified.

<html>
<head></head>
<body>
	// Maybe you have a menu here
	@yield('content')
	// Maybe a footer here
</body>
</html>



# In our next view, about i.e, (empty doc, the HTML is already defined in the layout)

@extends('layout')
@section('content')

	<h3>Some fancy text</h3>

@endsection


# Everything in the section part will now be piped into the layout, at the @yield place.


Database migration

# Remember to define the .env file in the root directory for your database credencials
# The DB also needs to be created it would seem.


# Say we would like to create a table in our DB that holds links
$ php artisan make:migration create_links_table --create=links


# The create_links_table is the file that gets created in database/migrations and
# links, after the --create, will be the name of the table.


# Then open the database/migration/_create_links_table document,
# In the up method we will be defining the columns.
# It should already hold the id and timestamp columns.

public function up()
{
	Schema::create('links', function (Blueprint $table) {
		$table->bigIncrements('id');
		$table->string('title');
		$table->string('url')->unique();
		$table->text('description');
		$table->timestamps();
	});
}


# Then run the migration
$ php artisan migrate


Lipsum (fake/test) data to the DB

# Quite good for testing I guess.
# These parts I don't fully understand yet, but fancy it was.


# So out above table, with a title, url and a description, let's fill that with test data
$ php artisan make:model --factory Link


# This will create a factory file in the database/factories folder, called LinkFactory.php
# Then plug in the the columns we've created, title, url and description

$factory->define(App\Link::class, function (Faker $faker) {
    return [
        'title'       => $faker->sentence(3),
        'url'         => $faker->url,
        'description' => $faker->paragraph
    ];
});


# Apparently the $faker model holds quite a few methods, properties and the like,
# to generate our test data - Awesome mayn.


# Alright, next up is to create a seeder.
$ php artisan make:seeder LinksTableSeeder


# This will create a file in database/seeds
# I don't understand this, but add this in the run() method

public function run()
{
    factory(App\Link::class, 5)->create();
}


# Guess it's something like, the factory class Link, and the numeric value is the number of rows.
# should be created (from a create() method I can't really find in the factory file.. Oh well)


# Anyway, to activate that seeder, you have to run it from the MAIN seeder file - DatabaseSeeder
# which is also found in the seeder folder. So add this to the run() method:

public function run()
{
    $this->call(LinksTableSeeder::class);
}


# Now that it's all set up, let's populate the table with some data!
$ php artisan migrate:fresh --seed


# Voila, you now have 5 rows of test data in the database table.


Routing w/ additional data (variables, arrays etc)

# Alright, so if we want to pass our newly created database data to the view
# Edit the route/web.php file (the MAIN routing doc)

Route::get('/', function () {
    // Collect the links
    $links = \App\Link::all();

    // Return them with the view
    return view('welcome', ['links' => $links] );
});


# The second argument can be an array of data, and the key-value ends up
# being the variable name in the template file.


# Other ways of doing this, instead of passing a second parameter

// with()
return view('welcome')->with('links', $links);

// dynamic method to name the variable
return view('welcome')->withLinks($links);


# Now edit the welcome.blade.php doc (view)
# For new projects you will of course have some HTML already, for the default page
# Just append this for the proof of concept

@foreach ($links as $link)
    <a href="{{ $link->url }}">{{ $link->title }}</a>
@endforeach



Create a submit form

# Continued with database we've already have, links
# First, create a new route in the you-know-what file (you should by now at least..)

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


# We're calling a /submit sub-page, so create a submit.blade.php in the view as well
# It actually seems this blade is a template engine


# Create a quick layout file in views/layouts/app.blade.php (create the folder if needed)

<html>
    <head>
        <title>App Name - @yield('title')</title>
    </head>
    <body>
        @section('sidebar')
            <!--This is the master sidebar.-->
        @show

        <div class="container">
            @yield('content')
        </div>
    </body>
</html>


# I'll break it down step by step, creating a blade template/ submit view
# There is quite a few goodies along the way.

@extends('layout.app') 
@section('content')

    <div class="container">
        <div class="row">
            <h1>Submit a link</h1>
            <form method="post" action="/submit">

            </form>
        </div>
    </div>

@endsection


# Pretty self-explained. Bootstrap seems available from the beginning.


Checking for User Input errors

# Here's a nice little goodie. If any errors is caught, then nicely display them.

<form method="post" action="/submit">

    @if ( $errors->any() )
        
        <div class="alert alert-danger" role="alert">
            Fix the following errors
        </div>

    @endif

</form>


# So, as you see, we are asking to fix the FOLLOWING errors.
# That is because, that each of the the form groups we are gonna create will have validation


# Alright, so let's have a look at one of our form groups - This one for the title

<div class="form-group{{ $errors->has('title') ? ' has-error' : '' }}">
    
    <label  for="title">Title</label>
    <input  type="text" 
            class="form-control" 
            id="title" 
            name="title" 
            placeholder="Title"
            value="{{ old('title') }}">
    
    @if( $errors->has('title') )
        <span class="help-block">{{ $errors->first('title') }}</span>
    @endif

</div>


# Each field/group will do a validation and display an error if has-error class is set for that group
# The (the value) will be the input that the user typed (stored in a session)
# If a field has an error, the $errors->first() will return the first error for that field


Continue with the submit form/page

# Now, because we aren't securing this form against cross-site request forgery,
# we'll exclude laravel from performing that security check, which apparently is default.
# Include this right after defining the form: {!! csrf_field() !!}


# So, all in all, the whole submit view document should look like this:

@extends('layouts.app') 
@section('content')

    <div class="container">
        <div class="row">
            <h1>Submit a link</h1>
            <form method="post" action="/submit">
                
                {!! csrf_field() !!}

                @if( $errors->any() )
                    <div class="alert alert-danger" role="alert">
                        Fix the following errors
                    </div>
                @endif

                <!-- Title -->
                <div class="form-group{{ $errors->has('title') ? ' has-error' : '' }}">
                    <label  for="title">Title</label>
                    <input  type="text" 
                            class="form-control" 
                            id="title" 
                            name="title" 
                            placeholder="Title"
                            value="{{ old('title') }}">
                    
                    @if( $errors->has('title') )
                        <span class="help-block">{{ $errors->first('title') }}</span>
                    @endif
                </div>


                <!-- URL -->
                <div class="form-group{{ $errors->has('url') ? ' has-error' : '' }}">
                    <label  for="title">URL</label>
                    <input  type="text" 
                            class="form-control" 
                            id="url" 
                            name="url" 
                            placeholder="URL"
                            value="{{ old('url') }}">
                    
                    @if( $errors->has('url') )
                        <span class="help-block">{{ $errors->first('url') }}</span>
                    @endif
                </div>


                <!-- Description -->
                <div class="form-group{{ $errors->has('description') ? ' has-error' : '' }}">
                    <label  for="title">Description</label>
                    <input  type="text" 
                            class="form-control" 
                            id="description" 
                            name="description" 
                            placeholder="Description"
                            value="{{ old('description') }}">
                    
                    @if( $errors->has('description') )
                        <span class="help-block">{{ $errors->first('description') }}</span>
                    @endif
                </div>

                <button type="submit" class="btn btn-default">Submit</button>

            </form>
        </div>
    </div>

@endsection



Route (and input validation continued)

# Now that our submit view page is created, our route document needs an additional method
# for the POST request. This new route method will also be the backbone for input validation

Route::post('/submit', function(Request $request) {
    $data = $request->validate([
        'title'       => 'required|max:255',
        'url'         => 'required|url|max:255',
        'description' => 'required|max:255'
    ]);

    $link = new App\Link($data);
    $link->save();

    // Alternative you could do like this:
    // $link = tap(new App\Link($data))->save();
    // 
    // The tab() allows to quickly create an instance of the model

    return redirect('/');
});


# So the $request model has a validate method.
# We are saying each is required, that each can be max 255 characters, and URL need to be
# validated as an URL. Voila. Peace of pie.


# As you can see, we are sending the $data forward to the Link model.
# If you were to open that document, app/Link.php, it's just an empty model - Let's fix.
# So we need to tell it which data we can populate it with:

class Link extends Model
{
    protected $fillable = ['title', 'url', 'description'];
}



# Now everything should be a-okay! The landing page should have the links, probably the
# 5 test rows from we've created earlier (database seeding) and if you navigate to
# /submit and fill out he form, it will create it to the database and send you back to the landing