Laravel 8 from Scratch
Link
https://laracasts.com/series/laravel-8-from-scratch
Course notes: Laracasts > Learn Laravel Path > Laravel 8 From Scratch
01 - An Animated Introduction to MVC
- The router loads the controller.
- The controller loads the information necessary to provide a response.
- Eloquent models are a good place to store data/object-related domain knowledge and business logic.
- The view generates the HTML structure using the data from the controller.
02 - Initial Environment Setup and Composer
- Prerequisites
- Installation - Laravel 8.x
- Note that the installation instructions on that page install the latest version (currently v10.10).
- Create a project in Laravel 8.x with Laravel Sail (docker)
03 - The Laravel Installer Tool
- After installing the
laravel/installer
package (preferably globally) using Composer (and adding~/.composer/vendor/bin/
to the PATH), you can runlaravel new DIR_NAME
to simplify the creation of a new project slightly.composer global require laravel/installer
04 - Why Do We Use Tools
- In general, to solve a problem.
- In the case of Laravel, to create a [web application](…/Web development/).
- In the case of this course, to create a blog.
05 - How a Route Loads a View
- Routes for the browser are defined in
./routes/web.php
. - The callback function can return a view, or also an object (for a JSON response).
06 - Include CSS and JavaScript
- Files in the
public
directory are static files available at the root of the application. - In real life, a CSS/JS bundler would be used to transform files in
resources
.
07 - Make a Route and Link to it
- Add a
Route::get
(or presumably, other method named after a HTTP method) invocation to./routes/web.php
.
08 - Store Blog Posts as HTML Files
file_get_contents()
is a PHP method that returns the contents of a file as a string.- Dynamic parts in routes are called “wildcards” and are wrapped in
{}
in the argument to a staticRoute
method, e.g.Route::get('posts/{slug}'
. dd()
is a Laravel helper method that stands for “die and dump”, useful for quick debugging.ddd()
is another Laravel helper method, displaying more information about the request (maybe it means “die, dump and debug”).
09 - Route Wildcard Constraints
Route::get()
returns an object with methods that can limit (constrain) what the wildcards in the URL are allowed to match, e.g. to avoid a security issue where user input is directly translated to a path, allowing access to arbitrary files on the server:where()
uses a [regular expression](…/Regular expressions/) to limit what the wildcard may matchwhereAlpha()
limits a wildcard to A-Z characterswhereNumber()
limits a wildcard to numbers
10 - Use Caching for Expensive Operations
- Helper methods available in routes definitions:
cache->remember()
caches data for a specific amount of seconds or until an explicit expiration date/time- The method both returns already cached data as well as stores not already cached data (by calling the callback in the third argument)
now()
returns the current date/time and by callingaddMinutes()
etc. on it, can be used to generate a time in the future (or past)redirect()
generates a redirect to another URL
11 - Use the Filesystem Class to Read a Directory
- Models are usually in the folder
./app/Models
and the namespaceApp\Models
. - Helper methods for getting absolute filesystem paths:
base_path()
->.
app_path()
->./app
resource_path()
->./resources
ModelNotFoundException
is a Laravel-provided exception that can be used when a model (record) is not found (e.g. in the database, filesystem, etc.).- The
Illuminate\Support\Facades\File
facade gives static access to methods for working with the file system. - If the class allows it, you can cast an object to a specific type by writing the desired type in parentheses before the object, e.g.
(string) $var
- The
SplFileInfo
class has a methodgetContents()
that returns the file contents as a string. - The PHP method
array_map
loops over an array, does something (callback) with each item, and returns an array of the “transformed” items.
12 - Find a Composer Package for Post Metadata
- If you find yourself iterating over an array and filling/returning a different array, that’s a use case for
array_map()
- or a Laravel collection. collect()
is a Laravel helper method that wraps an array in aIlluminate\Support\Collection
object.- A
Collection
has these methods, all of which may be chained.
13 - Collection Sorting and Caching Refresher
sortBy
sorts a Collection by an attribute of its items, ascending.sortByDesc
does the same, but sorts descending.cache()->rememberForever()
caches objects until they are cleared.- Other methods of the Cache are e.g.
put
,store
,forget
,get
.
14 - Blade: The Absolute Basics
- The Blade templating language is compiled down to PHP files stored in
./storage/framework/views
. - Only files with names ending with .blade.php are processed by Blade.
- The views are typically stored in
./resources/views
. - Raw PHP code can be used in Blade templates.
{{ $var }}
echoes a variable, sending it throughhtmlspecialchars
to escape (sanitize) HTML.
{!! $var !!}
echoes a variable without escaping HTML, similar to<?= $var; ?>
or<?php echo $var; ?>
.- Only use this for trusted (pre-sanitized) content to prevent XSS.
- There are a number of Blade directives, e.g.
@foreach
/@endforeach
,@if
/@endif
,@unless
/@endunless
. - The
@loop
variable makes it easy to style odd/even rows, treat the last/first iteration differently, etc. @dd
is available to die-and-dump variables.
15 - Blade Layouts Two Ways
- Template Inheritance
- Create a parent layout
.blade.php
file with@yield
directives (where the content should go) - Create a child view that
@extends
the layout - Use the
@section
/@endsection
directive in the child layout to provide the content
- Create a parent layout
- Blade components
- A Blade component wraps a piece of HTML.
- Components are
.blade.php
files typically in the./resources/views/components
directory.
- In a Blade component layout, slots are defined using the regular syntax for echoing out variables, e.g.
{{ $content }}
.
- Instead of
@extend
ing a layout, a child view uses the layout with a tag similar to HTML, where the component’s name (the part of the filename before.blade.php
) is prefixed withx-
. For example, if the layout was in./resources/views/components/layout.blade.php
, the child view would include this layout as follows:<x-layout> ... </x-layout>
- The variables that are echoed out in the layout can be passed through from the child view in different ways:
- As an attribute:
<x-layout content="..."> </x-layout>
- As a child tag:
<x-layout> <x-slot name="content"> ... </x-slot> </x-layout>
- As everything within the tag, if the slot (that is echoed out in the layout component) has the special name
$slot
:<html> <body> {{ $slot }} </body> </html>
<x-layout> ... </x-layout>
- As an attribute:
- None of the approaches is better than the other, but Blade components are newer and the remainder of the course will focus more on them.
16 - A Few Tweaks and Consideration
17 - Environment Files and Database Connections
- With the following
artisan
commands, when running locally, invoke them like this:php artisan COMMAND
- When using Sail (Docker containers managed by Laravel), invoke them like this:
./vendor/bin/sail artisan COMMAND
- The application’s configuration files are in the
./config
directory. - Most actual configuration values are set in the
./.env
file (which is environment-specific). - When using Sail, connect to the database within the database container using the command
./vendor/bin/sail mysql
(substitutemysql
withmariadb
,psql
orredis
as appropriate).
18 - Migrations: The Absolute Basics
- To create the initially defined database tables, run:
artisan migrate
- Database migrations are defined in subclasses of
Illuminate\Database\Migrations\Migration
in./database/migrations
. - The
up
method applies the migration and thedown
method reverses (rolls it back). - Applied migrations are logged in the
migrations
table. - Migrations are applied in batches (all the new ones since the last migration was done). Rolling back reverts the complete batch. To rollback the last applied batch:
artisan migrate:rollback
- To drop and re-create the database:
artisan migrate:fresh
- The APP_ENV value “production” has a special meaning for
artisan
, resulting in warnings when attempting destructive operations.
19 - Eloquent and the Active Record Pattern
- Eloquent is the official ORM of the Laravel framework.
- Each model has a corresponding database table. Each instance of a model represents a single record in the table. This is also called Active Record pattern.
- Tinker is a REPL (console) interface to a Laravel application, similar to the Django shell.
- Tinkerwell is a commercial GUI REPL interface for macOS, [Windows](…/Microsoft Windows/) and Linux.
bcrypt()
is a Laravel helper method that encrypts a string (e.g. for storing a password in the database).- To persist (store) a model to the database, call its
save()
method. - Eloquent automatically maintains the date/time columns
created_at
andupdated_at
.
20 - Make a Post Model and Migration
- To create a migration to create a new table “posts”, run:
artisan make:migration create_posts_table
- Laravel uses the argument (“create_posts_table”) as part of the migration filename, but also parses the desired table name “posts”.
- To create a model called “Post”, run:
artisan make:model Post
- Laravel convention: Use the singular name (“Post”) for the model class and the plural, lower-cased name (“posts”) for the database table.
21 - Eloquent Updates and HTML Escaping
- Stay with the default and escape displayed data, unless you (the developer(s)) are in control of it.
22 - 3 Ways to Mitigate Mass Assignment Vulnerabilities
- A mass assignment vulnerability happens when a malicious user sends an unexpected field in a HTTP request (e.g. is_admin set to
true
) and the developer then provides all request fields to the model for storing, unintentionally allowing the user to elevate their privileges. - To prevent such attacks, Eloquent models have a
fillable
property, containing the names of the attributes thay may be “mass assigned”. - The reverse option is to have a
guarded
property in the model, containing names of attributes that may not be mass assigned. - A third option is never to assign a generic array to the model for storing (build up the object manually).
23 - Route Model Binding
- If the wildcard in a route matches an argument to a callback function and that argument has a type which is an Eloquent model, Laravel assumes that the wildcard is the primary key, and automatically provides the fetched model to the callback function. For example, with the following code, with the HTTP request
GET https://host:port/posts/2
:Route::get('posts/{post}'), function (Post $post) { return view('post', [ 'post' => $post ]); });
- The view
./resources/views/post.blade.php
is loaded and the Post resulting from the querySELECT * FROM posts WHERE id = 2
is provided to it. - The behavior to use the primary key can be modified in the Model by overriding
public function getRouteKeyName()
and returning the value of the property that should be used instead.
- The view
- If the wildcard contains a colon, the part after the colon represents the (unique) property that should be used to fetch the model, instead of its primary key. For example, with the following code, with the HTTP request
GET https://host:port/posts/a-b-c
:Route::get('posts/{post:slug}'), function (Post $post) { return view('post', [ 'post' => $post ]); });
- The view
./resources/views/post.blade.php
is loaded and the Post resulting from the querySELECT * FROM posts WHERE slug = 'a-b-c' LIMIT 1
is provided to it.
- The view
24 - Your First Eloquent Relationship
- To add a relation, we need another model and migration. Previously we learned
artisan make:migration
andartisan make:model
. An easier way is to callartisan make:model -m
, which will create a migration at the same time. - To add the “foreign key”, or pointer from one model to another, we can create a public function with the desired name - e.g.
category()
- and let it return a relationship:class Post extends Model { // ... public function category() { return $this->belongsTo(Category::class); } }
- Now we can use
$post->category
(as if it was a property, thanks to magic accessors) to fetch the category pointed to by the value of thecategory_id
column in the table.
25 - Show All Posts Associated With a Category
- The opposite of a
belongsTo()
relationship ishasMany()
:class Category extends Model { // ... public function posts() { return $this->hasMany(Post::class); } }
26 - Clockwork, and the N+1 Problem
- We can log stuff using the
\Illuminate\Support\Facades\Log
facade. - An alternative is to use Laravel’s
logger()
helper function. - The default logging location is
./storage/logs/laravel.log
. - The
\Illuminate\Support\Facades\DB
facade canlisten()
to database queries. The argument to its callback function is a Query, with the SQL code (prepared statement) in itssql
property and actual query parameters in itsbindings
property. - The Composer package itsgoingd/clockwork can be used to log database queries and more, and make them available in the browser during debugging.
- By design, Laravel lazy-loads relationships (i.e. the reference/ID/foreign key is loaded from the referencing table, but nothing is loaded from the referenced table).
- The method
with()
on an Eloquent model can be used to tell which relationship(s) we want to load together with the model to avoid the N+1 problem.
27 - Database Seeding Saves Time
- The directory
./database/seeders
contains DatabaseSeeder, which allows populating the database (e.g. after the initial migration) - making it easier to modify the database structure during development without losing the sample data necessary for testing. - The models
factory()
method can be used in a Seeder to create a number of objects with random dummy data for testing. - The artisan command
db:seed
invokes the Seeder. So does the--seed
argument to the artisan commandmigrate:fresh
. - The models
truncate()
method deletes all objects of that model (truncating the database table).
28 - Turbo Boost With Factories
- The
User
Model uses the traitIlluminate\Database\Eloquent\Factories\HasFactory
to add the staticfactory()
method mentioned in the previous lesson. - Its behavior is defined in
./database/factories/UserFactory.php
. - The
UserFactory
class has a propertymodel
indicating which model it can create data for (the User model). - Any new Model created with Artisan gets the
HasFactory
trait - but in order for the factory to work, by convention, one has to create a corresponding class in./database/factories
. - The artisan command
make:factory
can be used to create such a class. - The same result can be achieved by using the
--factory
argument for the artisan commandmake:model
. - When giving
artisan make:model
the argument--all
, in addition to the model, a migration, seeder, factory and resource controller are being scaffolded. - The factory method
definition()
should return an array with the keys and values that should be set on the “random model”. - The keys that reference other models (e. g. “user_id”, “category_id”, etc.) can have an invocation of the corresponding models factory method as their value, effectively creating a new dummy instance of that other model for each dummy instance of the model.
- Individual properties of the dummy instances created by the factories’
create()
method can be set to desired values by providing those as an array to thecreate()
method. (The other properties defined in the factory class’definition()
will still be generated randomly.)
29 - View All Posts By An Author
- Laravel uses the names of relationship functions in models to discover the foreign key by appending “_id”, for example the following code assumes that the table
posts
has a columnuser_id
which also is a foreign key to another table (presumably calledusers
):class Post extends Model { public function user() { return $this->belongsTo(User::class); } }
- However this convention can be overridden by providing the actual name of the column/foreign key as the second argument to
belongsTo()
.
30 - Eager Load Relationships on an Existing Model
- Using the
$with
property in a Model, we can force Eloquent to always load related models (i.e., from other tables referenced via foreign keys) together with that model. - Beware that this results in extra queries by default, which can hurt performance if there are situations where the related data is not needed.
- In such cases the
without()
method can be used to selectively disable the eager-loading of specific relationships.
31 - Convert the HTML and CSS to Blade
32 - Blade Components and CSS Grids
- Add a colon before the attribute name to pass the value of a variable to a Blade component. Such an attribute is called a “prop” and needs to be declared at the top of the component using the directive
@props()
. - Attributes (not props) can be accessed within a Blade component using the
$attributes
array.You may specify which attributes should be considered data variables using the
@props
directive at the top of your component’s Blade template. All other attributes on the component will be available via the component’s attribute bag. - Timestamps are instances of Carbon, which builds upon PHP functions to simplify working with date/time objects (similar to Moment.js in JavaScript).
- Carbon provides the method
diffForHumans()
to format the relative difference between now and a timestamp in a human-readable way (e.g. “1 day ago”). - The
skip()
method can be used to skip the first X items of a collection when iterating over its items.
33 - Convert the Blog Post Page
34 - A Small JavaScript Dropdown Detour
- Add
style="display:none"
to avoid flashing content that would be hidden by JavaScript after the page is loaded. - The
ucwords()
PHP function converts the first character of each word in a string to uppercase. - The method
is()
on a model instance can be used to compare it to another instance using the primary key, presumably because the===
operator does not work if the database row has been fetched twice.
35 - How to Extract a Dropdown Blade Component
- When using a Blade component, all children that are not wrapped in
<x-slot>...</x-slot>
get rendered in the default slot called$slot
. - The Request instance returned by the request() helper can be used to inspect the request.
- Its is() method compares the current request path to a string (which may contain wildcards).
- It may be safer to use its
routeIs()
method to compare it to a named route.
36 - Quick Tweaks and Clean-Up
{{-- ... --}}
comments out stuff in Blade templates.
- The PHP method implode() joins array elements with a string and returns a string.
37 - Search (The Messy Way)
- The
get()
method, typically at the end of a chain of Eloquent calls, can be interpreted as “We are done, execute the SQL queries”.
38 - Search (The Cleaner Way)
- Controllers can be scaffolded with the artisan command
make:controller
. By convention, their name ends withController
and starts with a singular noun. They are located in the./app/Http/Controllers
directory. - Instead of a callback function, the second argument to the Route methods (
get
, etc.) can be an array with the Controller class and the name of the controller method that should be called for this route. - Query scopes add methods to Eloquent models that filter the rows queried from the database. The names of such methods have to start with
scope
, e.g.public function scopeFilter()
. When using this (assumed) method in an Eloquent query, only the part afterscope
, with the first letter lower-cased, is used, e.g.:MyModel::latest()->filter();
- Query scope methods receive the current query as an argument.
- They are equal to filtering the results “manually” using the
where()
method, but make the code easier to read (and encourage reuse). - The argument (e.g.
$query
) can be modified as is, and does not have to be returned by the query scope method.
- If the
request()
helper is provided an array of strings, it returns an associative array of those keys from the request with their corresponding values. - Alternatively, the request returned from
request()
(when called without arguments) has anonly()
method that also returns an array of specific keys/values from the request. - Eloquent queries have a
when()
method that evaluates if a condition is truthy and only then invokes a callback with the query and the “truthy value” (e.g. to modify the query if the user has entered a search term).
39 - Advanced Eloquent Query Constraints
- The second argument to the Eloquent
where()
method is expected to be a value. If it should reference a column, usewhereColumn()
instead. whereHas()
is an easier method to filter by a value from a referenced table.- The
where()
andfirst()
methods can be combined intofirstWhere()
.
40 - Extract a Category Dropdown Blade Component
- The artisan command
make:component
creates a component (*.blade.php
file) and associates a PHP class in the directory./app/View/Components
with it. - That class has a
render()
method that can provide a view model to a component, similar to how a route provides one to a view. - You should typically follow the naming convention CONTROLLER (PLURAL) . ACTION FUNCTION for views, i.e. if the Controller is called “PostController” and the function (as indicated by the route) is called “show”, the view should be called “posts.view”.
- Each dot represents a directory, i.e. the view “posts.view” corresponds to the template
./resources/views/posts/view.blade.php
.
41 - Author Filtering
42 - Merge Category and Search Queries
- The PHP function
http_build_query
converts an array to a querystring. - The
except()
method of the Request object (as e.g. returned by therequest()
helper) returns all request parameters except the one(s) with the provided name(s).
43 - Fix a Confusing Eloquent Query Bug
- To group multiple
WHERE
clauses in parentheses (in the resulting SQL query), group them in awhere()
method and provide a closure (anonymous function) that receives the$query
as its sole argument and adds thewhere()
calls that should be grouped.
44 - Laughably Simple Pagination
- When you return an Eloquent object from a route, Laravel converts it to JSON.
- The
view()->paginate()
method takes the request param?page
into account, and provides all the logic (I18N strings, current and total page number, etc.) required to display a pager. - The Eloquent model has a
links()
method that returns pre-rendered HTML for a pager, including Tailwind classes. - The artisan command
vendor:publish
copies the resource files shipped by a Composer package from the./vendor/...
directory to the./resources/...
directory, so they can be customized for the app. - The
boot()
method in theAppServiceProvider
class (in./app/Providers/AppServiceProvider.php
) can be used to configure application-wide settings, such as whether the Paginator should use Bootstrap or Tailwind for styling. - The
view()->simplePaginate()
method takes the request param?page
into account, and provides logic to display a simple pager (just “Previous” and “Next”, no page numbers - which also makes it a bit more performant). - The Paginator method
withQueryString()
takes the filters in the querystring into account when rendering the pager.
45 - Build a Register User Page
- The Request’s
validate()
method accepts an associative array of form fields and validation rules. Multiple validation rules can be separated by the|
character or provided as an array of multiple strings, e.g.request()->validate([ 'name' => 'required|max:255', 'password' => ['required', 'min:7'] ]);
- If validation…
- fails: Laravel redirects the user back to the form;
- succeeds: the method returns the validated attributes.
- The
@csrf
(Blade) directive generates and outputs a token to prevent CSRF attacks as a hidden form input field.
46 - Automatic Password Hashing With Mutators
Illuminate\Support\Facades\Hash::check()
checks whether some plain text matches a hashed string.- Side notes about facades:
A facade is a class wrapping a complex library to provide a simpler and more readable interface to it.
Facades provide a “static” interface to classes that are available in the application’s service container. Laravel ships with many facades which provide access to almost all of Laravel’s features.
Laravel facades serve as “static proxies” to underlying classes in the service container, providing the benefit of a terse, expressive syntax while maintaining more testability and flexibility than traditional static methods.
- An Eloquent mutator “pipes” a value through a certain function whenever it is set (stored).
- By convention, a mutator function is called setATTRIBUTE_NAMEAttribute, e.g. “setPasswordAttribute” for a function that hashes a password before storing it.
- It should set
$this->attributes['ATTRIBUTE_NAME']
to the mutated value.
- The opposite of a mutator is an accessor.
- Such functions are named getATTRIBUTE_NAMEAttribute, e.g. “getUsernameAttribute”.
- They should return the “demutated”, desired value.
47 - Failed Validation and Old Input Data
- The
@error
Blade directive (followed by@enderror
) provides validation errors for the form input with the given name. - Inside those two directives, the
$message
attribute can be rendered to output the actual error message. - The helper method
old()
provides the “old” values (from the previous form submission) so the invalid form can be re-rendered without the user having to type everything again. - The variable
$errors
is set to an “error bag” containing all validation errors that occurred during a form submission.- It always exists, even if there are no validation errors.
- Its
all()
method returns all errors, which can then be iterated over, e.g. with aforeach
statement, to show all errors in one place. - Its
any()
method returns whether there are any errors to display.
48 - Show a Success Flash Message
- The
session()->flash()
method stores something (e.g. flash messages) in the session for one page request only. - The
session()
helper method or thesession()->get()
method retrieve a value with a given key from the session storage. - The
redirect()
method can be chained with awith()
method, which is shorthand for flashing a message.
49 - Login and Logout
- The
auth()
helper is functionally equivalent to theAuth
facade. auth()->login($user)
logs the provided user in.- Middleware does something with requests before they reach the core of your application logic.
- The
middleware()
method (e.g. in./routes/web.php
) adds middleware to a route. - The middleware
'guest'
ensures that a route does not work for logged-in users. - Two types of middleware:
- Global, run for all requests, specified in the property
$middlewareGroups
in./app/Http/Kernel.php
- Route-specific, can optionally be added to a route, specified in the property
$routeMiddleware
in./app/Http/Kernel.php
- Global, run for all requests, specified in the property
- The Blade directive
@guest
/@endguest
renders content for anonymous users only, while@auth
/@endauth
renders content for signed-in users only. - The signed-in user can be retrieved using
auth()->user()
. auth()->logout()
destroys the user’s session.
50 - Build the Log In Page
auth()->attempt()
attempts to log the user in with the provided credentials.- The
back()
helper redirects to the previous URL, maybe->withErrors()
(a developer-provided array of form elements and corresponding error messages) and->withInput()
(the user’s input to the original form). - An even simpler approach is to throw a
ValidationException
using its static constructor::withMessages
and provide the form elements that fail validation and corresponding error messages, which will automatically flash the user’s original input to the session for redisplay. - To avoid the attack called “session fixation”, a user’s session ID should be regenerated whenever the user gets logged in.
51 - Laravel Breeze Quick Peek
- Laravel Breeze is an authentication starterkit that provides predesigned Blade templates styled with Tailwind CSS.
- It can be installed into an existing project using
composer require laravel/breeze --dev
followed byartisan breeze:install
.
52 - Write the Markup for a Post Comment
53 - Table Consistency and Foreign Key Constraints
- In migrations:
- The
id()
method creates columns of the type “unsignedBigInteger”, so a “manually created” foreign key column that references another table’sid
column - if created by theid()
method - must be of that type. - The
foreignId()
method automatically creates a column of the correct type. - The chainable
constrained()
method automatically references the correct table for a foreign key, if the convention of calling the column OTHERTABLENAME_id is followed. Otherwise the name of the referenced table should be provided. - The chainable
cascadeOnDelete()
method is shorthand foronDelete('cascade')
.
- The
54 - Make the Comments Section Dynamic
55 - Design the Comment Form
- When you find yourself reusing the same Tailwind classes over and over, consider extracting them to a Blade component.
56 - Activate the Comment Form
- If possible, when creating Controller methods, try to stick to “the seven RESTful actions”:
- index
- show
- create
- store
- edit
- update
- destroy
- As an alternative to using the
request()
helper method, one can inject theRequest
class into a Controller methods arguments. - To avoid being protected from the mass assignment injection attack, either
- Set
$guarded = [];
in models, or - call
Model::unGuard()
e.g. inAppServiceProvider->boot()
. - When doing this, remember not to create/update models using all request parameters (without filtering them).
- Set
57 - Some Light Chapter Clean Up
- Convention: Begin the filename of a partial view (that is included in another Blade file) with an underscore.
[58 - Mailchimp API Tinkering]
- All configuration is kept in files in the
./config
directory. - API tokens for external services are usually kept in
./config/services.php
. - The
config()
helper provides access to values from the files in the./config
directory, using dot notation - e.g. if to retrieve the value of the keykey
in the arraymailchimp
in the file./config/services.php
, one would ask forconfig('services.mailchimp.key')
.
59 - Make the Newsletter Form Work
60 - Extract a Newsletter Service
- Automatic resolution is a Laravel feature to inject dependencies into methods in many places instead of having to instantiate them manually, e.g. some Service into a Route’s action method.
- The null-safe assignment operator
??=
assigns the right-side value to the left-side variable only if the left-side variable is null. - When specifying a Controller to handle a route without specifying an action (method) name, the Controller’s
__invoke()
method will be called. We could call these “single-action Controllers”.
61 - Toy Chests and Contracts
- If automatic resolution fails because a constructor requires a parameter that can’t simply be instantiated, Laravel will throw a
\Illuminate\Contracts\Container\BindingResolutionException
. - The
AppServiceProvider->register()
method is a logical place to to register objects into the service container usingapp()->bind()
. - Objects can be retrieved from the service container using
app()->get()
or the shorthand helper methodresolve()
.
62 - Limit Access to Only Admins
- The class
\Symfony\Component\HttpFoundation
contains constants for all HTTP status codes.
63 - Create the Publish Post Form
64 - Validate and Store Post Thumbnails
- Files uploaded by users are instances of
\Illuminate\Http\UploadedFile
. - Filesystem “disks” are configured in
./config/filesystems.php
and can be local, accessed via SFTP, on AWS S3 etc. artisan storage:link
creates symbolic links from the./public
directory (the served webroot) to stored files. These links are also configured at the bottom of./config/filesystems.php
.- The
asset()
helper method prepends the protocol and domain to a given path.