Complete learning path from SQL basics to advanced Laravel CRUD with Eloquent ORM, Query Builder, Relationships, and Joins
SQL Basics & Joins
Core PHP with MySQL
Laravel Models & Relationships
Eloquent ORM CRUD
Query Builder & Advanced Joins
SQL Joins combine rows from two or more tables based on a related column between them. Essential for relational databases.
+----+------------------+-------------+ | id | name | location | +----+------------------+-------------+ | 1 | ABC University | New York | | 2 | XYZ University | California | | 3 | PQR University | Texas | +----+------------------+-------------+
+----+------------------+---------------+ | id | name | university_id | +----+------------------+---------------+ | 1 | Engineering | 1 | | 2 | Business | 1 | | 3 | Medical | 2 | | 4 | Arts | NULL | +----+------------------+---------------+
| Join Type | Description | SQL Syntax | Result Data |
|---|---|---|---|
| INNER JOIN | Returns only matching rows from both tables | SELECT * FROM universities INNER JOIN colleges ON universities.id = colleges.university_id |
Universities that have colleges + their colleges |
| LEFT JOIN | Returns all rows from left table + matching rows from right table | SELECT * FROM universities LEFT JOIN colleges ON universities.id = colleges.university_id |
All universities + their colleges (NULL if no college) |
| RIGHT JOIN | Returns all rows from right table + matching rows from left table | SELECT * FROM universities RIGHT JOIN colleges ON universities.id = colleges.university_id |
All colleges + their universities (NULL if no university) |
| FULL OUTER JOIN | Returns all rows when there's a match in either table | SELECT * FROM universities FULL OUTER JOIN colleges ON universities.id = colleges.university_id |
All universities and all colleges |
| CROSS JOIN | Returns Cartesian product of both tables | SELECT * FROM universities CROSS JOIN colleges |
Every university combined with every college |
+------------------+-------------+------------------+ | university_name | location | college_name | +------------------+-------------+------------------+ | ABC University | New York | Engineering | | ABC University | New York | Business | | XYZ University | California | Medical | +------------------+-------------+------------------+
+------------------+-------------+------------------+ | university_name | location | college_name | +------------------+-------------+------------------+ | ABC University | New York | Engineering | | ABC University | New York | Business | | XYZ University | California | Medical | | PQR University | Texas | NULL | +------------------+-------------+------------------+
+------------------+-------------+------------------+ | university_name | location | college_name | +------------------+-------------+------------------+ | ABC University | New York | Engineering | | ABC University | New York | Business | | XYZ University | California | Medical | | NULL | NULL | Arts | +------------------+-------------+------------------+
Before Laravel, understand how PHP directly interacts with MySQL using mysqli or PDO. Laravel's Eloquent and Query Builder abstract these operations.
<?php
// Core PHP MySQL Connection
$host = 'localhost';
$user = 'root';
$password = '';
$database = 'university_db';
$connection = mysqli_connect($host, $user, $password, $database);
if (!$connection) {
die("Connection failed: " . mysqli_connect_error());
}
?>
<?php
// Core PHP - Create University
include 'config.php';
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
$name = mysqli_real_escape_string($connection, $_POST['name']);
$location = mysqli_real_escape_string($connection, $_POST['location']);
$sql = "INSERT INTO universities (name, location) VALUES ('$name', '$location')";
if (mysqli_query($connection, $sql)) {
echo "University created successfully!";
} else {
echo "Error: " . mysqli_error($connection);
}
}
?>
<!-- HTML Form -->
<form method="POST">
<input type="text" name="name" placeholder="University Name" required>
<input type="text" name="location" placeholder="Location" required>
<button type="submit">Create University</button>
</form>
<?php
// Core PHP - Read Universities with Colleges (INNER JOIN)
include 'config.php';
$sql = "SELECT
u.name as university_name,
u.location,
c.name as college_name
FROM universities u
INNER JOIN colleges c ON u.id = c.university_id
ORDER BY u.name";
$result = mysqli_query($connection, $sql);
if (mysqli_num_rows($result) > 0) {
while($row = mysqli_fetch_assoc($result)) {
echo "University: " . $row['university_name'] . "<br>";
echo "Location: " . $row['location'] . "<br>";
echo "College: " . $row['college_name'] . "<br><hr>";
}
} else {
echo "No records found";
}
?>
<?php
// Core PHP - Update University
include 'config.php';
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
$id = $_POST['id'];
$name = mysqli_real_escape_string($connection, $_POST['name']);
$location = mysqli_real_escape_string($connection, $_POST['location']);
$sql = "UPDATE universities SET name='$name', location='$location' WHERE id=$id";
if (mysqli_query($connection, $sql)) {
echo "University updated successfully!";
} else {
echo "Error: " . mysqli_error($connection);
}
}
?>
<?php
// Core PHP - Delete University
include 'config.php';
if (isset($_GET['delete_id'])) {
$id = $_GET['delete_id'];
$sql = "DELETE FROM universities WHERE id=$id";
if (mysqli_query($connection, $sql)) {
echo "University deleted successfully!";
} else {
echo "Error: " . mysqli_error($connection);
}
}
?>
Models in Laravel represent database tables and use Eloquent ORM for object-relational mapping. They provide an ActiveRecord implementation.
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class University extends Model
{
protected $fillable = ['name', 'location'];
// One-to-Many relationship: University has many Colleges
public function colleges() {
return $this->hasMany(College::class);
}
// Accessor for formatted name
public function getNameAttribute($value) {
return ucwords($value);
}
// Query Scope for active universities
public function scopeActive($query) {
return $query->where('is_active', true);
}
}
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class College extends Model
{
protected $fillable = ['name', 'university_id'];
// Many-to-One relationship: College belongs to University
public function university() {
return $this->belongsTo(University::class);
}
// One-to-Many relationship: College has many Departments
public function departments() {
return $this->hasMany(Department::class);
}
}
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Department extends Model
{
protected $fillable = ['name', 'college_id'];
// Many-to-One relationship: Department belongs to College
public function college() {
return $this->belongsTo(College::class);
}
}
// This creates N+1 queries (inefficient)
$universities = University::all();
foreach($universities as $university) {
echo $university->colleges->count(); // Separate query for each university
}
// This creates only 2 queries (efficient)
$universities = University::with('colleges')->get();
foreach($universities as $university) {
echo $university->colleges->count(); // No additional queries
}
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Models\University;
use Illuminate\Support\Facades\Validator;
class UniversityEloquentController extends Controller
{
// CREATE - Show form
public function create() {
return view('universities.create');
}
// CREATE - Store data
public function store(Request $request)
{
$validator = Validator::make($request->all(), [
'name' => 'required|string|max:255',
'location' => 'required|string|max:255'
]);
if($validator->fails()) {
return redirect()->back()->withErrors($validator)->withInput();
}
University::create([
'name' => $request->name,
'location' => $request->location
]);
return redirect()->route('universities.view')->with('success', 'University added successfully.');
}
// READ - View all universities with relationships
public function view() {
return view('universities.view', [
'universities' => University::with('colleges.departments')->orderBy('id','desc')->get()
]);
}
// UPDATE - Show edit form
public function edit($id) {
return view('universities.edit', [
'university' => University::findOrFail($id)
]);
}
// UPDATE - Process update
public function update(Request $request, $id)
{
$validator = Validator::make($request->all(), [
'name' => 'required|string|max:255',
'location' => 'required|string|max:255'
]);
if($validator->fails()) {
return redirect()->back()->withErrors($validator)->withInput();
}
University::findOrFail($id)->update([
'name' => $request->name,
'location' => $request->location
]);
return redirect()->route('universities.view')->with('success', 'University updated successfully.');
}
// DELETE - Remove university
public function destroy($id)
{
University::findOrFail($id)->delete();
return redirect()->back()->with('success', 'University deleted successfully.');
}
// JOIN Example with Eloquent
public function joinUniversitiesColleges()
{
return University::join('colleges', 'universities.id', '=', 'colleges.university_id')
->select('universities.name as university_name', 'colleges.name as college_name')
->get();
}
}
// Create university with college
$university = University::create(['name' => 'Tech University', 'location' => 'Boston']);
$college = $university->colleges()->create(['name' => 'Engineering']);
// Eager loading with nested relationships
$universities = University::with(['colleges' => function($query) {
$query->where('name', 'like', '%Engineering%');
}])->get();
// Accessing relationships
foreach($universities as $university) {
echo $university->name;
foreach($university->colleges as $college) {
echo $college->name;
foreach($college->departments as $department) {
echo $department->name;
}
}
}
// WhereHas for filtering by relationship
$engineeringUniversities = University::whereHas('colleges', function($query) {
$query->where('name', 'like', '%Engineering%');
})->get();
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Validator;
class UniversityQueryBuilderController extends Controller
{
// CREATE
public function store(Request $request)
{
$validator = Validator::make($request->all(), [
'name' => 'required|string|max:255',
'location' => 'required|string|max:255'
]);
if($validator->fails()) {
return redirect()->back()->withErrors($validator)->withInput();
}
DB::table('universities')->insert([
'name' => $request->name,
'location' => $request->location,
'created_at' => now(),
'updated_at' => now()
]);
return redirect()->route('qb.universities.view')->with('success', 'University added successfully.');
}
// READ
public function view() {
return view('universities.view', [
'universities' => DB::table('universities')->orderBy('id','desc')->get()
]);
}
// UPDATE
public function update(Request $request, $id)
{
$validator = Validator::make($request->all(), [
'name' => 'required|string|max:255',
'location' => 'required|string|max:255'
]);
if($validator->fails()) {
return redirect()->back()->withErrors($validator)->withInput();
}
DB::table('universities')->where('id', $id)->update([
'name' => $request->name,
'location' => $request->location,
'updated_at' => now()
]);
return redirect()->route('qb.universities.view')->with('success', 'University updated successfully.');
}
// DELETE
public function destroy($id) {
DB::table('universities')->where('id', $id)->delete();
return redirect()->back()->with('success', 'University deleted successfully.');
}
// RIGHT JOIN Example (not easily possible with Eloquent)
public function rightJoinUniversitiesColleges()
{
return DB::table('universities')
->rightJoin('colleges', 'universities.id', '=', 'colleges.university_id')
->select('universities.name as university_name', 'colleges.name as college_name')
->get();
}
// Aggregation with GROUP BY
public function universityCollegeCount()
{
return DB::table('universities')
->join('colleges', 'universities.id', '=', 'colleges.university_id')
->select('universities.name', DB::raw('COUNT(colleges.id) as total_colleges'))
->groupBy('universities.name')
->get();
}
// Complex Multiple Joins
public function universityCollegeDepartmentJoin()
{
return DB::table('universities')
->join('colleges', 'universities.id', '=', 'colleges.university_id')
->leftJoin('departments', 'colleges.id', '=', 'departments.college_id')
->select(
'universities.name as university_name',
'colleges.name as college_name',
'departments.name as department_name'
)
->get();
}
}
// Equivalent to: SELECT * FROM universities INNER JOIN colleges ON universities.id = colleges.university_id
$results = DB::table('universities')
->join('colleges', 'universities.id', '=', 'colleges.university_id')
->select('universities.name as university_name', 'colleges.name as college_name')
->get();
// Get all universities even if they have no colleges
$results = DB::table('universities')
->leftJoin('colleges', 'universities.id', '=', 'colleges.university_id')
->select('universities.name as university_name', 'colleges.name as college_name')
->get();
// Count colleges per university with having clause
$results = DB::table('universities')
->join('colleges', 'universities.id', '=', 'colleges.university_id')
->select('universities.name', DB::raw('COUNT(colleges.id) as college_count'))
->groupBy('universities.id', 'universities.name')
->having('college_count', '>', 1)
->get();
<?php
// routes/web.php
use App\Http\Controllers\UniversityEloquentController;
use App\Http\Controllers\UniversityQueryBuilderController;
// Eloquent ORM Routes
Route::prefix('eloquent')->group(function() {
Route::get('universities/create', [UniversityEloquentController::class, 'create'])->name('universities.create');
Route::post('universities/store', [UniversityEloquentController::class, 'store'])->name('universities.store');
Route::get('universities', [UniversityEloquentController::class, 'view'])->name('universities.view');
Route::get('universities/edit/{id}', [UniversityEloquentController::class, 'edit'])->name('universities.edit');
Route::put('universities/update/{id}', [UniversityEloquentController::class, 'update'])->name('universities.update');
Route::delete('universities/delete/{id}', [UniversityEloquentController::class, 'destroy'])->name('universities.destroy');
});
// Query Builder Routes
Route::prefix('query')->group(function() {
Route::get('universities/create', [UniversityQueryBuilderController::class, 'create'])->name('qb.universities.create');
Route::post('universities/store', [UniversityQueryBuilderController::class, 'store'])->name('qb.universities.store');
Route::get('universities', [UniversityQueryBuilderController::class, 'view'])->name('qb.universities.view');
Route::get('universities/edit/{id}', [UniversityQueryBuilderController::class, 'edit'])->name('qb.universities.edit');
Route::put('universities/update/{id}', [UniversityQueryBuilderController::class, 'update'])->name('qb.universities.update');
Route::delete('universities/delete/{id}', [UniversityQueryBuilderController::class, 'destroy'])->name('qb.universities.destroy');
});
// API Routes for mobile apps
Route::prefix('api')->group(function() {
Route::apiResource('universities', UniversityApiController::class);
});
universities.create - Show create formuniversities.store - Store new record (POST)universities.view - List all recordsuniversities.edit - Show edit formuniversities.update - Update record (PUT)universities.destroy - Delete record (DELETE)<!-- resources/views/universities/create.blade.php -->
@extends('layouts.app')
@section('title', 'Add University')
@section('content')
<div class="container mx-auto px-4 py-8">
<div class="max-w-2xl mx-auto">
<h1 class="text-3xl font-bold text-gray-800 mb-6">Add New University</h1>
@if ($errors->any())
<div class="bg-red-100 border border-red-400 text-red-700 px-4 py-3 rounded mb-4">
<ul>
@foreach ($errors->all() as $error)
<li>{{ $error }}</li>
@endforeach
</ul>
</div>
@endif
<form action="{{ route('universities.store') }}" method="POST" class="bg-white shadow-md rounded px-8 pt-6 pb-8 mb-4">
@csrf
<div class="mb-4">
<label class="block text-gray-700 text-sm font-bold mb-2" for="name">
University Name
</label>
<input class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline"
id="name" name="name" type="text" placeholder="Enter university name" value="{{ old('name') }}" required>
</div>
<div class="mb-6">
<label class="block text-gray-700 text-sm font-bold mb-2" for="location">
Location
</label>
<input class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline"
id="location" name="location" type="text" placeholder="Enter location" value="{{ old('location') }}" required>
</div>
<div class="flex items-center justify-between">
<button class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded focus:outline-none focus:shadow-outline" type="submit">
Create University
</button>
<a href="{{ route('universities.view') }}" class="inline-block align-baseline font-bold text-sm text-blue-500 hover:text-blue-800">
Back to List
</a>
</div>
</form>
</div>
</div>
@endsection
<!-- resources/views/universities/view.blade.php -->
@extends('layouts.app')
@section('title', 'Universities List')
@section('content')
<div class="container mx-auto px-4 py-8">
<div class="flex justify-between items-center mb-6">
<h1 class="text-3xl font-bold text-gray-800">Universities</h1>
<a href="{{ route('universities.create') }}" class="bg-green-500 hover:bg-green-700 text-white font-bold py-2 px-4 rounded">
Add New University
</a>
</div>
@if(session('success'))
<div class="bg-green-100 border border-green-400 text-green-700 px-4 py-3 rounded mb-4">
{{ session('success') }}
</div>
@endif
<div class="bg-white shadow-md rounded">
<table class="min-w-full">
<thead>
<tr class="bg-gray-200 text-gray-600 uppercase text-sm leading-normal">
<th class="py-3 px-6 text-left">ID</th>
<th class="py-3 px-6 text-left">Name</th>
<th class="py-3 px-6 text-left">Location</th>
<th class="py-3 px-6 text-left">Colleges</th>
<th class="py-3 px-6 text-center">Actions</th>
</tr>
</thead>
<tbody class="text-gray-600 text-sm font-light">
@forelse($universities as $university)
<tr class="border-b border-gray-200 hover:bg-gray-100">
<td class="py-3 px-6 text-left whitespace-nowrap">{{ $university->id }}</td>
<td class="py-3 px-6 text-left">{{ $university->name }}</td>
<td class="py-3 px-6 text-left">{{ $university->location }}</td>
<td class="py-3 px-6 text-left">
@foreach($university->colleges as $college)
<span class="bg-blue-100 text-blue-800 text-xs font-medium mr-2 px-2.5 py-0.5 rounded">
{{ $college->name }}
</span>
@endforeach
</td>
<td class="py-3 px-6 text-center">
<div class="flex item-center justify-center">
<a href="{{ route('universities.edit', $university->id) }}" class="w-4 mr-2 transform hover:text-purple-500 hover:scale-110">
<i class="fas fa-edit"></i>
</a>
<form action="{{ route('universities.destroy', $university->id) }}" method="POST" class="inline">
@csrf
@method('DELETE')
<button type="submit" class="w-4 mr-2 transform hover:text-red-500 hover:scale-110" onclick="return confirm('Are you sure?')">
<i class="fas fa-trash-alt"></i>
</button>
</form>
</div>
</td>
</tr>
@empty
<tr>
<td colspan="5" class="py-4 px-6 text-center">No universities found.</td>
</tr>
@endforelse
</tbody>
</table>
</div>
</div>
@endsection
with() to prevent N+1 queries$fillable or $guardedYES! But with limitations:
join() method)leftJoin())// Eloquent JOIN (limited)
University::join('colleges', 'universities.id', '=', 'colleges.university_id')
->select('universities.name', 'colleges.name')
->get();
// Query Builder JOIN (full control)
DB::table('universities')
->rightJoin('colleges', 'universities.id', '=', 'colleges.university_id')
->leftJoin('departments', 'colleges.id', '=', 'departments.college_id')
->select('universities.name', DB::raw('COUNT(departments.id) as dept_count'))
->groupBy('universities.id')
->get();
with()) to avoid N+1 queries