Laravel
Routing
Resources
Create index and store routes
Route::resource('chirps', ChirpController::class)
'index', 'store', 'update', 'destroy'])
->only(['auth', 'verified']); ->middleware([
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
index | GET | get the view at the route | /resource |
create | GET | display the form to create a new _ | /resource/create |
store | POST | save new resources to the db | /resource |
show | GET | show something (but not a full page, that's index) | /resource/{id} |
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); ; ])'can:create,App\Models\User'); })->middleware(
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);
"job: " . $job) logger(
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
Create an AWS account
Go to name in the top right -> Security Credentials -> get an access key
Generate one
aws configure --profile <name> aws-profile
Launch an EC2 instance
terraform init # in project root terraform plan terraform apply
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
Add CI/CD
EC2_HOST
secret is in the ssh command Add the full ssh keyBuy the domain namecheap.com
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
on namecheap: Account -> Dashboard -> domain list
Domain -> nameservers -> Custom DNS -> paste in the 4 from Route 53 (remove the periods at the end)
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