- Laravel
- Setting up a project
- Hosting on DigitalOcean
- Basic examples
- Blade
- blade directives in forms
- Connect with DB via Laravel
- Using Models in Controller - Miscellaneous
routes/api.php
- redirect
- if route only needs to return a view
- route parameters
- error
Add [...] to fillable property to allow mass assignment on [...].
- implicit model binding
- access fields from foreign records
- routes middleware
- Policies vs gates
- Upload file
- Accessor
- Automatic UUID generation
- Custom validation error messages
- Validation
composer create-project laravel/laravel project-name
php artisan serve
git clone ...
composer install
php artisan key:generate
cp .env.example .env
, open.env
and editDB_*
variablesphp artisan migrate
php artisan serve
- install LAMP stack using guide
- during Setting an apache virtual host, create a virtual host with any name as described in the tutorial and use the following
/etc/apache2/sites-available/*.conf
:
<VirtualHost *:80>
ServerName 24.199.126.176
ServerAdmin webmaster@localhost
DocumentRoot /var/www/laravel/public
<Directory /var/www/laravel>
AllowOverride All
</Directory>
ErrorLog ${APACHE_LOG_DIR}/error.log
CustomLog ${APACHE_LOG_DIR}/access.log combined
</VirtualHost>
- before restarting apache, run
sudo a2enmod rewrite
- follow section
- add site IP/domain to
APP_URL
at.env
- to grant proper permission run the following (replace
PROJECT_NAME
with you project name):
sudo chown -R www-data.www-data /var/www/PROJECT_NAME/storage
sudo chown -R www-data.www-data /var/www/PROJECT_NAME/bootstrap/cache
- no need to
php artisan serve
, just go tohttp://server_domain_or_IP
- run
php artisan make:controller MyController
to create fileapp/Http/Controllers/MyController.php
and edit:
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
class MyController extends Controller {
public function homepage() {
return "<h1>Homepage</h1>";
}
}
- edit
routes/web.app
:
<?php
use Illuminate\Support\Facades\Route;
use App\Http\Controllers\MyController;
Route::get('/', [MyController::class, 'homepage']);
touch resources/views/myview.blade.php
and edit it:
@foreach($vars as $var)
<h3>{{ $var }}</h3>
@endforeach
return view('myview', ['vars' => ['foo', 'bar', 'baz']]);
from a controller
@include('header')
- create a component inside
resources/views/components/
<header>...</header>
{{ $slot }}
<footer>...</footer>
- use it:
<x-layout>
<main>...<main>
</x-layout>
- any HTML forms pointing to
POST
,PUT
,PATCH
, orDELETE
routes that are defined in the web routes file should include a CSRF token field - add
@csrf
inside form, it will be replaced by hidden input with name equal totoken
@method('DELETE')
- create database
- edit db-related variables at
.env
php artisan migrate
php artisan migrate:fresh
= to drop all tables and re-run all migrations (NOTE: will lose data)- alter structure without losing data
php artisan make:migration add_favorite_color_column
- add to
up
method:Schema::table('users', function (Blueprint $table) { $table->string('favoriteColor'); });
- add to
down
method:Schema::table('users', function (Blueprint $table) { $table->dropColumn('favoriteColor'); });
php artisan migrate
// usually done in the controller method that handles the form submission
$fields = $request->validate([
'username' => ['required', 'min:3', 'max:5', Rule::unique('users', 'username')],
'email' => ['required', 'email', Rule::unique('users', 'email')],
'password' => ['required', 'confirmed']
]);
// if validation fails, redirect back to page and errors will be available in the error bag
- field type
confirmed
('password' => ['required', 'confirmed']
) must have 2 inputs, one with idpassword
and other with idpassword_confirmation
<div class="form-group">
<label for="username-register" class="text-muted mb-1"><small>Username</small></label>
<input name="username" id="username-register" class="form-control" type="text" placeholder="Pick a username" autocomplete="off" />
</div>
@error('username')
<div class="alert alert-danger">{{ $message }}</div>
@enderror
<ul class="input-error">
@foreach ($errors->get('celular') as $message)
<li class="input-error">{{ $message }}</li>
@endforeach
</ul>
<div class="form-group">
<label for="email-register" class="text-muted mb-1"><small>Email</small></label>
<input name="email" id="email-register" class="form-control" type="text" placeholder="[email protected]" autocomplete="off" value="{{old('email')}}" />
</div>
<h2>Hello <strong>{{ auth()->user()->username }}</strong>, your feed is empty.</h2>
// inside controller
return redirect('/')->with('success', 'Logged in successfully');
// inside view (*.blade.php)
@if (session()->has('success'))
<p>{{ session('success') }}</p>
@endif
<input name="username" placeholder="Pick a username" @if (old('username') !== null) value="{{old('username')}}" @endif />
// `/api` URI prefix is automatically applied
// `match` and `any` are used to respond to multiple HTTP verbs
Route::match(['get', 'post'], '/foo', function () {
return 'ok';
});
redirect('/bar');
= redirect inside callbackRoute::redirect('/foo', '/bar', 301);
= top-level redirect insideweb.php
Route::view('/welcome', 'welcome', ['name' => 'Taylor']);
Route::get('/user/{id}', function (Request $request, string $id) {
return 'User '.$id;
});
Route::get('/posts/{post}/comments/{comment}', [MyController::class, comment]);
- in controller, access it with
$request->post
,$request->comment
- in controller, access it with
- add to model class
protected $fillable = ['prop1', 'prop2', ...];
// web.php
// `{post}` is expected to hold the primary key,
// to specify any other field use `{user:username}`
Route::get('/posts/{post}', [PostController::class, 'showPost']);
// PostController.php
use App\Models\Post;
class PostController extends Controller
{
// route parameter and its respective controller method parameter must have matching names
public function showPost(Post $post)
{
return $post->title; // $post is automatically fetched from db
}
}
// app/Models/Post.php
use App\Models\User;
class Post extends Model
{
public function user()
{
return $this->belongsTo(User::class, 'user_id');
}
}
// post.blade.php
<p>Posted by <a href="#">{{$post->user->username}}</a></p>
// web.php
Route::get('/', [UserController::class, 'showCorrectHomepage'])->name('login');
// `auth` middleware will redirect to route named `login` if user is not logged in
Route::get('/create-post', [PostController::class, 'showCreatePostForm'])->middleware('auth');
// `guest` middleware: if logged in, redirect to route define as `HOME` at `RouteServiceProvider`
Route::get('/login', 'login')->middleware('guest');
- gates are more suitable for handling fine-grained authorization logic for specific actions
- while policies are used to manage CRUD authorization logic for entire resources (models)
- generate policy =
php artisan make:policy PostPolicy --model=Post
- register policy = in
App\Providers\AuthServiceProvider
add to$policies
:Post::class => PostPolicy::class,
- edit
app/Policies/PostPolicy.php
- how to use:
- in blade template =
@can ('update', $post)
- in controller =
auth()->user()->can('delete', $post)
- in route (as middleware) =
Route::get('/posts/{post}/edit', [PostController::class, 'showEditPostForm'])->middleware('can:update,post')
- in blade template =
- define a gate inside
boot
method inapp/Providers/AuthServiceProvider.php
public function boot(): void
{
Gate::define('visitAdminPages', function ($user) {
return $user->isAdmin === 1;
});
}
- usage
- in controller:
Gate::allows('visitAdminPages')
- in route (as middleware):
->middleware('can:visitAdminPages')
- in controller:
- form must contain attribute
enctype="multipart/form-data"
$request->file('nameAttrValue')
= access image sent in controller$request->file('avatar')->store('public/avatars')
= file will be stored atstorage/app/public/avatars
php artisan storage:link
= create symbolic link tostorage/app/
inpublic/
to make uploaded files publicly accessible
// inside a Model, create a method with the same name as a field
// access property with $user->avatar (don't call it as a function)
protected function avatar(): Attribute
{
return Attribute::make(get: function ($value) {
return $value ? '/storage/avatars/' . $value . '.jpg' : 'fallback.png';
});
}
https://www.educative.io/answers/how-to-use-uuids-in-laravel
- unlike auto incrementing unsigned integer keys, the UUID field doesn't just populate itself
- at migration:
$table->uuid('id')->primary();
// add to your model
public static function boot()
{
parent::boot();
static::creating(function ($model) {
if ($model->getKey() === null) {
$model->setAttribute($model->getKeyName(), Str::uuid()->toString());
}
});
}
public function getIncrementing ()
{
return false;
}
public function getKeyType()
{
return 'string';
}
- official docs
- we can customize the error messages by passing a second array as an argument to
validate()
$fields = $request->validate([
'cpf' => 'required|regex:/^\d*$/',
], [
'cpf.regex' => 'A inscrição municipal deve conter apenas números',
]);
missing
= must not be present in the input dataprohibited
= must be missing or empty (null
, empty string or empty array)
https://laravel.com/docs/10.x/validation#specifying-custom-messages-in-language-files
php artisan lang:publish
= to getresources/lang/en/validation.php
- in
app/config.php
:
'locale' => 'pt_BR',
// show error messages in english when there's no translation for pt_BR
'fallback_locale' => 'en',
- copy to
resources/lang/pt_BR/validation.php
- translate needed error messages