Laravel

Routing

Resources

Create index and store routes

Route::resource('chirps', ChirpController::class)
    ->only(['index', 'store', 'update', 'destroy'])
    ->middleware(['auth', 'verified']);

These create routes for the components: ex. 'chirps.destroy'

Update example

The dependency injection parses the chirp from the resource route /chirps/{chirp} and injects it

You don't have to call the method. Laravel's routing does that automatically when the parameters match

class ChirpController extends Controller
{
  public function update(Request $request, Chirp $chirp): RedirectResponse
    {
    }
}

// Expects chirp to be an id
Route::patch('/chirps/{chirp}', [ChirpController::class, 'update'])->name('chirp.update');

// Or you can resolve by name instead of id: dependency injection will resolve the tag by name instead of id
Route::get('/tags/{tag:name}');

Middleware

  • Examples: Rate-Rate Limiting, logging, caching, CSRF, email-verified users
  • Define middleware groups in app/Http/Kernel.php

Wrap routes in a middleware group:

Route::middleware('guest')->group(function () {
  Route::get('register', [RegisteredUserController::class, 'create'])->name('register');

  Route::post('register', [RegisteredUserController::class, 'store']);
});

Controllers

Naming conventions

indexGETget the view at the route
createGETdisplay the form to create a new _
storePOSTsave new resources to the db
showGETshow something (but not a full page, that's index)

POST requests automatically include CSRF protection. So any route that modifies server state should be a POST request

Database Interactions

Use TablePlus for a database GUI

# php artisan tinker
App\Models\Chirp::all();

Relationships, Models

Establishing relationships lets you do things like:

$request->user()->chirps()->create($validated);

// app/Models/User.php
class User extends Authenticatable
{
    // Enable mass assignment for this attribute
    protected $fillable = [
        'message',
    ];

    public function chirps()
    {
        return $this->hasMany(Chirp::class);
    }
}

class Chirp extends Model
{
    // Access as a property, not a method. Ex. $chirp->user
    public function user()
    {
        return $this->belongsTo(User::class);
    }
}
  • hasOne

Migrations

<?php

return new class extends Migration
{
    public function up(): void
    {
        Schema::create('chirps', function (Blueprint $table) {
            $table->id();
            // constrained means that user_id has to exist in it's table
            $table->foreignId('user_id')->constrained()->cascadeOnDelete();
            $table->string('message');
            $table->timestamps();
        });
    }
};

Factories

Used to seed your DB

php artisan tinker
> App\Models\Job::factory()->create();

# or
> App\Model\Job::factrory()->unverified()->create();

Eloquent

// lazy load - can cause the N+1 problem
$jobs = Jobs::all();
$jobs[0]->salary;

Job::create(['title' => 'Director', 'salary' => '$1,000,000']);

Job::find(7)->delete(); // find id 7 then delete it

Job::first();

// ex. if job belongsTo employer
// latest is essentially an ORDER BY
Job::with('employer')->latest()->simplePaginate(3);

$tag = Tag::firstOrCreate(['name' => $name]);
$this->tags()->attach($tag);

Job::where('title', 'LIKE', '%'.request('q').'%');

// Eager load to avoid n+1 problem
Job:with(['employer', 'tags']);

// You can keep everything on it's own line
Job::query()
    ->with()
    ->where();

Artisan

php artisan make:model -mrc Chirp

php artisan migrate:fresh # reset the database

Policies

  • No policy class => everything is allowed

  • Policy class => Nothing is allowed except what's specified in the policies

  • Lets you define rules for controller commands

    class PostPolicy
    {
        public function update(User $user, Post $post): bool
        {
            return $user->id === $post->user_id;
        }
    }
    
    // in the route
    Route::get('users/create', function() {
        Return Inertia::render('Users/Create', [
            'can' => Auth::user()->can('create', User::class);
        ]);
    })->middleware('can:create,App\Models\User');

Events

  • Add listeners to things that happen
php artisan make:listener SendChirpCreatedNotifications --event=ChirpCreated

ext: https://bootcamp.laravel.com/inertia/notifications-and-events

Useful Functions

$job = Arr::first($jobs, fn($job) => $job['id'] == $id);

logger("job: " . $job)

Request Validation

$userAttributes = $request->validate([
    'name' => ['required'],
    'email' => ['required', 'email', 'unique:users,email'], // verify that it's unique on the users table
    'password' => ['required', Password::min(6)],
    'schedule' => ['required', Rule::in(['Part Time', 'Full Time'])],
]);

$user = User::create($userAttributes);

Auth::login($user);

Deployment

  1. Create an AWS account

  2. Go to name in the top right -> Security Credentials -> get an access key

  3. Generate one

    aws configure --profile <name>
    aws-profile
  4. Launch an EC2 instance

    terraform init # in project root
    terraform plan
    terraform apply
  5. SSH into ec2

    aws configure
    
    # on ec2
    ssh-keygen -t rsa -b 4096
    cat ~/.ssh/id_rsa.pub # copy it
    
    # on host
    gha
    paste >> tmp
    gh ssh-key add tmp --title <name>
    rm tmp
    
    # back to ec2
    sudo yum install git -y
    git clone git@github.com:lanceberge/<name>.git
    
    cd <proj>
    ./scripts/setup_ec2
  6. Add CI/CD

    EC2_HOST secret is in the ssh command Add the full ssh key

  7. Buy the domain namecheap.com

  8. Route 53 -> Create hosted zone with the domain -> create

    Create two A records: blank -> public IP and www -> public IP

    • change the TTL of the nameservers to 60
  9. on namecheap: Account -> Dashboard -> domain list

    Domain -> nameservers -> Custom DNS -> paste in the 4 from Route 53 (remove the periods at the end)

  10. Once the domain propagates

    sudo certbot --nginx -d <domain.com> -d www.<domain.com>
    
    rm nginx/nginx.conf
    sudo mv /etc/nginx/nginx.conf nginx/nginx.conf
    sudo ln -s "/home/ec2-user/$PROJ_NAME/nginx/nginx.conf" /etc/nginx/nginx.conf