API REST de cursos con Laravel 11 — ejemplo completo

Pequeño ejemplo de un controlador Laravel que devuelve la lista de cursos publicados como JSON. Úsalo para probar el resaltado de sintaxis con Prism.

1. Migración

<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class extends Migration {
    public function up(): void
    {
        Schema::create('courses', function (Blueprint $table) {
            $table->id();
            $table->string('title');
            $table->string('slug')->unique();
            $table->text('description')->nullable();
            $table->enum('level', ['beginner', 'intermediate', 'advanced']);
            $table->unsignedInteger('price_cents')->default(0);
            $table->boolean('is_published')->default(false);
            $table->timestamps();
        });
    }

    public function down(): void
    {
        Schema::dropIfExists('courses');
    }
};

2. Modelo Eloquent

El modelo usa casts para tipar is_published y un scope para filtrar publicados.

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;

class Course extends Model
{
    protected $fillable = [
        'title', 'slug', 'description', 'level', 'price_cents', 'is_published',
    ];

    protected $casts = [
        'is_published' => 'boolean',
        'price_cents'  => 'integer',
    ];

    public function scopePublished(Builder $query): Builder
    {
        return $query->where('is_published', true);
    }

    public function getPriceAttribute(): float
    {
        return $this->price_cents / 100;
    }
}

3. Controlador

<?php

namespace App\Http\Controllers;

use App\Models\Course;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;

class CourseController extends Controller
{
    public function index(Request $request): JsonResponse
    {
        $level = $request->string('level')->toString();

        $courses = Course::query()
            ->published()
            ->when($level, fn ($q) => $q->where('level', $level))
            ->orderByDesc('created_at')
            ->limit(20)
            ->get(['id', 'title', 'slug', 'level', 'price_cents']);

        return response()->json([
            'data'  => $courses,
            'count' => $courses->count(),
        ]);
    }
}

4. Ruta

use App\Http\Controllers\CourseController;
use Illuminate\Support\Facades\Route;

Route::get('/api/courses', [CourseController::class, 'index'])
    ->name('courses.index');

5. Probar con curl

curl -s "http://mi-app.test/api/courses?level=beginner" | jq

6. Respuesta JSON

{
  "data": [
    {
      "id": 1,
      "title": "Curso de Laravel de cero",
      "slug": "curso-de-laravel-de-cero",
      "level": "beginner",
      "price_cents": 4900
    }
  ],
  "count": 1
}

7. Blade de ejemplo

@extends('layouts.app')

@section('content')
    <h1>Cursos disponibles</h1>

    <ul class="grid grid-cols-3 gap-4">
        @forelse ($courses as $course)
            <li class="rounded-xl border p-4 shadow">
                <a href="{{ route('courses.show', $course) }}">
                    {{ $course->title }}
                </a>
                <span class="badge">{{ $course->level }}</span>
            </li>
        @empty
            <li>Aún no hay cursos publicados.</li>
        @endforelse
    </ul>
@endsection

Pulsa el botón Copy que aparece arriba a la derecha de cada bloque para copiar el código al portapapeles.

<?php while ( have_posts() ) : the_post(); ?>

    <article id="post-<?php the_ID(); ?>" <?php post_class(); ?>>
        <h2><?php the_title(); ?></h2>
        <div class="entry-content">
            <?php the_content(); ?>
        </div>
    </article>

<?php endwhile; ?>
<p>No hay publicaciones que mostrar.</p>