We’re going to learn Laravel by walking through the user story. The user story parts will be underlined. You can view all of the code for this course on git: https://github.com/davidrichied/laravel-notes.
The user goes to the home page of the website and sees a button that says Create Note.
Routes
First of all, how does our application know what to display when the user visits the home page of the website?
In Laravel, you define routes (paths) that point to a function in a class.
These routes live in a PHP file called web.php inside of a folder called routes.
This route gets triggered when a user hits the home page of the website.
Route::name('home')->get('/', [\App\Http\Controllers\HomeController::class, 'show']);
In other frameworks, setting up a route is a lot more verbose.
We have the option of naming our routes (thanks to the Route class) which we will do because it’s very useful.
If you were wondering, the get method (Route::name(‘home’)->get) corresponds to the type of request we’re making. In web development, there are a couple important request types to know. In addition to GET, there’s also POST, PATCH, and DELETE, but we’ll learn about those later. The get method receives two parameters. The first parameter is a path which is just a slash (/) since this is the home page.
The second parameter is the class and function to call when someone visits the path. If we make a get request to the ‘/’ path, this route will be triggered, and the class/function combo in the second parameter will be called.
The class (HomeController) in the second parameter is called a controller, and that’s exactly where we’re headed next.
Controllers
A controller is the function that’s called when someone visits a certain web page. It gets the data (the model) and returns it to the view (the presentation layer). It’s really what connects the data to the view, and that’s probably why it’s called the controller.
The controller classes live in the app folder (~/Code/appone/app/Http/Controllers). The app folder will contain the majority of the code.
Laravel has a special command you can use to create controllers. This is the command that I used to create the home controller. There’s nothing special about the HomeController.php file that it creates. The command only creates that one file. We could’ve just as easily created it manually.
php artisan make:controller HomeController
return view('welcome');
The show function in the HomeController class returns the view. Instead of returning a view, the controller function can also return JSON.
Views
Views live inside of the resources folder (~/Code/appone/resources/views). In the MVC framework, the view is the presentation layer. A view’s purpose is to display data. You could say that the view is dumb. There shouldn’t be too much logic in the views.
Blade
Your view files could be plain PHP, but Laravel uses a package called Blade. You can think of blade as a cross between HTML and PHP. The blade template for the home page is pretty simple.
The user sees the button that says “Create a Note” and clicks it.
He goes to the page for creating notes.
That triggers another route in our application.
Route::name('note-create')->get('/notes/create', [\App\Http\Controllers\NoteController::class, 'create']);
This our second route. It’s also a simple GET request. The route is notes/create. The class is NoteController, and the function is create. In Laravel, if we have a specific page for creating something (like a note), the function for displaying that page should always be called create. It doesn’t need to be called create. That’s just a naming convention in Laravel.
In the create function, we return a blade template. It’s a good idea to familiarize yourself with forms for this section.
The user sees the form for creating a note.
He fills it out and clicks the “Add Note” button.
A couple things are going to happen here. Actually, a lot is going to happen. The form’s action is pointing to another route, and we are using the POST method on the form. When the user submits the form, the form makes a POST request to the route.
Route::name('notes-store')->post('/notes', [\App\Http\Controllers\NoteController::class, 'store']);
In the web. php file, the third route uses the post method instead of the get method. Note that there are two routes with the same path, but Laravel knows which one to call based on the type of request (get or post).
The route points to a function in the note controller called store. That name is another convention. When a function is creating a resource (a row in a database table), it’s called store.
public function store(Request $request)
The $request variable has a lot of useful data. The data from the form is available in the request object. The Laravel Request class takes care of managing requests (like the post we just made using the form).
$noteData = [ 'title' => $request->get('title'), 'content' => $request->get('content'), ];
In the store function, we create an array of the data from the form. The $request object has a helpful method called “get”. We can access any data sent from a form using the get method. The parameter corresponds to whatever the name attribute of the input is (<input name=”title” />).
$note = Note::create($noteData);
We pass the array ($noteData) to the Note class’s create method. This is a quick way to add a new record (row) to a database table.
We call the Note class a Model because it models data in the database. Models are a big part of Laravel and MVC frameworks in general.
Models
In MVC frameworks, models are used to represent tables in a database. Laravel uses a package called Eloquent that has a lot of useful methods. Eloquent models (like Note) are used to create relationships between tables, get records from the database, save records, update then, and delete them. Basically,
Models have everything to do with our application’s data.
The Eloquent model class also makes use of a Laravel class called Collection. The Collection class has more useful functions for manipulating data from a database.
Most of the time, a model represents a row in a specific database table, but how did table get there in the first place?
We created it using a Laravel migration.
Migrations
In Laravel, we modify our database using migrations.
Take a look at the migrations folder inside of the database folder. All of those PHP files are updates to the database. One of those files has the updates for the notes table.
We created the file using a Laravel command.
php artisan make:migration create_notes_table
After running the command, we got a file with a name like 1111_11_11_111111_create_notes_table.php in the migrations folder (~/Code/appone/database/migrations).
Schema::create('notes', function (Blueprint $table) { $table->id(); $table->timestamps(); $table->string('title'); $table->longText('content'); });
In the migration file, you can see we are using the Schema class to create a new database table. We use another class called Blueprint to modify the table and its fields. The Blueprint class has a lot of different methods that you can chain together.
$table->id();
We need a field to identify the records (rows) in the table. This field will auto-increment each time we create a new record.
$table->timestamps();
Calling the timestamps method will add two fields to the table (created_at and updated_at). Laravel updates these fields accordingly when you create a new record and update it using Eloquent.
$table->string(‘title’);
This is one of our fields here. We’re adding a field called “title” and making it of a string type. The $table object has a different method for each different field type.
$table->longText(‘content’);
The longText method is for longText fields.
After we run this migration, we could look at the structure of the table (using a program like SequelPRO) and see these different types.
We could manually create our tables, manually create our fields, and manually specify the field types, but we will use Laravel to manage that for us.
php artisan migrate
Running the migrate command causes any new migrations to run. Laravel keeps tracks of which migrations have run in a database table that it manages.
Using Laravel to manage our database updates has huge benefits, like rolling back changes, and creating databases for testing, but we’ll look at that later.
Schema::dropIfExists('notes');
Below the code for creating the table is some code for undoing the changes. In this case, all we have to do is drop the table.
Note Model
Now that we know what the Note model is referring to, we can pick up where we left off.
The Note model has a couple important properties and functions. I have included a couple properties that are set by default to help you see how to override defaults.
The $table property is a string that corresponds to the name of the table in the database (notes).
The $fillable property tells Laravel which fields are “mass assignable.” Without specifying those, we wouldn’t be able to update our fields all at once, like we did when using the create method (Note::create($noteData);).
Editing a Note
redirect('/notes/' . $note->id . '/edit');
After we create the new note, we send the user to the page for editing his note. That triggers another route (/notes/{note}/edit).
Route::name('notes-edit')->get('/notes/{note}/edit', [\App\Http\Controllers\NoteController::class, 'edit']);
‘/notes/{note}/edit’
The path has some curly braces, but those aren’t part of the literal path. All the previous paths were literal. The curly braces and the word inside represent a RegEx. We could put anything we wanted in place of the curly braces, and this route would fire.
/notes/hello/edit
/notes/random/edit
/notes/1/edit
Any of the above paths would trigger this route, but only the last one will do anything.
The note that we created was assigned an ID. If you look at the rows in the database table, you’ll see the new record, along with all of the fields (ID, created_at, updated_at, title, content).
return redirect('/notes/' . $note->id . '/edit');
We’re dynamically constructing the path using the note’s ID in the store method.
The user is now on the edit page.
The user makes a couple changes to the note and clicks the Update button.
Route::name('notes.patch')->patch('/notes/{note}', [\App\Http\Controllers\NoteController::class, 'update']);
This post request hits the notes.patch route.
<form action="{{route('notes.patch',['note' => $note->id])}}" method="post"> {{ csrf_field() }} {{ method_field('PATCH') }} <div> <label for="title">Title</label> <input type="text" name="title" value="{{ $note->title }}"/> </div> <div> <label for="content">Content</label> <textarea name="content">{{ $note->content }}</textarea> </div> <div> <input type="submit" value="Update Note"/> </div> </form>
action={{route(‘notes.patch’,[‘note’ => $note->id])}}
The action is using Laravel’s route function to get the correct route.
‘notes.patch’
The first parameter is the name that we gave the route.
[‘note’ => $note->id]
The second parameter is an array containing key/value pairs. The key is the variable name we put in the curly braces ({note}), and the value is what we want to put in its place.
The line would return something like /notes/2
{{ method_field(‘PATCH’) }}
We also have to use Laravel’s method_field function to fake a PATCH request. This just dumps out an input field that Laravel uses to determine if this is a PATCH request.
value=”{{ $note->title }}”
We’re setting the input’s value using the note; otherwise, the input would be blank.
public function update(Request $request, Note $note) { $noteData = [ 'title' => $request->get('title'), 'content' => $request->get('content'), ]; $note->update($noteData); return redirect('/notes/' . $note->id . '/edit'); }
The update function is similar to the create function.
update(Request $request, Note $note)
In the parameter, you’ll see we are “type hinting” the $note variable. When we do that, Larval grabs the record from the database that has the ID in The URL, and it builds out the Note model that we can use in the function.
In the function, we create an array of data. We won’t do anything fancy, and we’ll just update all the data (title and content), just like we did when creating the note.
$note->update($noteData);
Without specifying the fields in the $fillable property, we wouldn’t have been able to use the useful update method.
return redirect(‘/notes/’ . $note->id . ‘/edit’);
We return the user to the page that he was on.
Viewing All Notes
The user clicks the My Notes link in the menu.
Route::name('notes.index')->get('/notes', [\App\Http\Controllers\NoteController::class, 'index']);
The page with all the notes hits the notes.index route. Index is another naming convention that’s used for a page that displays all of the results for a model.
return view('notes.index', ['notes' => Note::limit(15)->get()]);
In the index function, we’re getting a max of fifteen notes and returning them to the notes.index blade template.
He sees his updated note.
Deleting a Note
He clicks the delete button.
<form method="POST" action="/notes/{{$note->id}}"> {{ csrf_field() }} {{ method_field('DELETE') }} <div class="form-group"> <input type="submit" class="btn btn-danger delete-user" value="Delete note"> </div> </form>
We are creating a kind of hacky way to delete a note. The delete button is actually a small form with a single button that submits the form. The form makes a DELETE request. This is the last route in this small application.
Route::name('notes.destroy')->delete('/notes/{note}', [\App\Http\Controllers\NoteController::class, 'destroy']);
The delete route uses the delete method which means that it will expect a DELETE request, and that’s exactly what our form is sending. The ID of the note is also included in the route.
public function destroy(Request $request, Note $note) { $note->delete(); return redirect('/notes'); }
The route points to a function called destroy in the Note Controller.
In the destroy function, we call the delete method on the model and return the user to the page that he was on.
The user sees his page refresh, and his note is gone.
That’s it! Congratulations! This sets the foundation for your time with Laravel. You also gained a good understanding of MVC frameworks and MVC in general.
Now, it’s time to create your own project.