Laravel
Routing
Resources
Create index and store routes
resource('chirps', ChirpController::class)
Route::(['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
('/chirps/{chirp}', [ChirpController::class, 'update'])->name('chirp.update');
Route::patch
// Or you can resolve by name instead of id: dependency injection will resolve the tag by name instead of id
('/tags/{tag:name}'); Route::get
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:
('guest')->group(function () {
Route::middleware('register', [RegisteredUserController::class, 'create'])->name('register');
Route::get
('register', [RegisteredUserController::class, 'store']);
Route::post); }
Controllers
Naming conventions
index | GET | get the view at the route |
create | GET | display the form to create a new _ |
store | POST | save new resources to the db |
show | GET | show 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
{('chirps', function (Blueprint $table) {
Schema::create$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;
(['title' => 'Director', 'salary' => '$1,000,000']);
Job::create
(7)->delete(); // find id 7 then delete it
Job::find
();
Job::first
// ex. if job belongsTo employer
// latest is essentially an ORDER BY
('employer')->latest()->simplePaginate(3);
Job::with
$tag = Tag::firstOrCreate(['name' => $name]);
$this->tags()->attach($tag);
('title', 'LIKE', '%'.request('q').'%');
Job::where
// Eager load to avoid n+1 problem
(['employer', 'tags']);
Job:with
// 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 ('users/create', function() { Route::getReturn 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);
("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);
($user); Auth::login
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