Skip to content

Instantly share code, notes, and snippets.

@gusalbukrk
Last active August 29, 2023 03:07
Show Gist options
  • Save gusalbukrk/b25ac85c4eb773a2746a1065da2bfa9c to your computer and use it in GitHub Desktop.
Save gusalbukrk/b25ac85c4eb773a2746a1065da2bfa9c to your computer and use it in GitHub Desktop.
#laravel

Laravel

Setting up a project

Creating new project

  • composer create-project laravel/laravel project-name
  • php artisan serve

Cloning existing project

  • git clone ...
  • composer install
  • php artisan key:generate
  • cp .env.example .env, open .env and edit DB_* variables
  • php artisan migrate
  • php artisan serve

Hosting on DigitalOcean

  • 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 to http://server_domain_or_IP

Basic examples

Create controller

  • run php artisan make:controller MyController to create file app/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']);

Create view

  • 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

Blade

Include files

Method 1 - @include

  • @include('header')

Method 2 - <x-component>

  • create a component inside resources/views/components/
<header>...</header>
{{ $slot }}
<footer>...</footer>
  • use it:
<x-layout>
    <main>...<main>
</x-layout>

blade directives in forms

@csrf (solve 419 HTTP error)

  • any HTML forms pointing to POST, PUT, PATCH, or DELETE 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 to token

form method other than GET and POST

  • @method('DELETE')

Connect with DB via Laravel

  • 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

Using Models in Controller - Miscellaneous

form validation

// 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

password confirmation field

  • field type confirmed ('password' => ['required', 'confirmed']) must have 2 inputs, one with id password and other with id password_confirmation

display error message

display first error message of specific field

<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

display all error messages of specific field

<ul class="input-error">
    @foreach ($errors->get('celular') as $message)
        <li class="input-error">{{ $message }}</li>
    @endforeach
</ul>

old() - repopulating form fields after submission failure

<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>

get username

<h2>Hello <strong>{{ auth()->user()->username }}</strong>, your feed is empty.</h2>

display flash messages

// inside controller
return redirect('/')->with('success', 'Logged in successfully');
// inside view (*.blade.php)
@if (session()->has('success'))
    <p>{{ session('success') }}</p>
@endif

conditionally display attribute

<input name="username" placeholder="Pick a username" @if (old('username') !== null) value="{{old('username')}}" @endif />

routes/api.php

// `/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

  • redirect('/bar'); = redirect inside callback
  • Route::redirect('/foo', '/bar', 301); = top-level redirect inside web.php

if route only needs to return a view

  • Route::view('/welcome', 'welcome', ['name' => 'Taylor']);

route parameters

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

error Add [...] to fillable property to allow mass assignment on [...].

  • add to model class protected $fillable = ['prop1', 'prop2', ...];

implicit model binding

// 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
    }
}

access fields from foreign records

// 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>

routes middleware

// 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');

Policies vs gates

  • 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)

Policy

  • 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')

Gates

  • define a gate inside boot method in app/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')

Upload file

  • 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 at storage/app/public/avatars
    • php artisan storage:link = create symbolic link to storage/app/ in public/ to make uploaded files publicly accessible

Accessor

    // 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';
        });
    }

Automatic UUID generation

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';
    }

Custom validation error messages

$fields = $request->validate([
    'cpf' => 'required|regex:/^\d*$/',
], [
    'cpf.regex' => 'A inscrição municipal deve conter apenas números',
]);

Validation

prohibited vs missing

  • missing = must not be present in the input data
  • prohibited = must be missing or empty (null, empty string or empty array)

translate error messages

https://laravel.com/docs/10.x/validation#specifying-custom-messages-in-language-files

  • php artisan lang:publish = to get resources/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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment