php-template/docs/getting-started.md
Snider ef4379a76f
Some checks are pending
CI / PHP 8.2 (push) Waiting to run
CI / PHP 8.3 (push) Waiting to run
CI / PHP 8.4 (push) Waiting to run
CI / Assets (push) Waiting to run
docs: add comprehensive documentation for core-template
Add documentation covering architecture, modules, getting started,
and security considerations for the Core PHP Framework starter template.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-29 21:21:11 +00:00

6.6 KiB

title description updated
Getting Started Quick start guide for creating a new Core PHP Framework application 2026-01-29

Getting Started

This guide walks you through creating your first application with Core PHP Framework using the core-template.

Prerequisites

  • PHP 8.2 or higher
  • Composer 2.x
  • Node.js 18+ and npm
  • SQLite (default) or MySQL/PostgreSQL

Installation

1. Clone the Template

git clone https://github.com/host-uk/core-template.git my-project
cd my-project

Or use Composer create-project (once published):

composer create-project host-uk/core-template my-project

2. Install Dependencies

# PHP dependencies
composer install

# JavaScript dependencies
npm install

3. Configure Environment

# Copy environment file
cp .env.example .env

# Generate application key
php artisan key:generate

# Create SQLite database
touch database/database.sqlite

# Run migrations
php artisan migrate

4. Start Development Server

# In one terminal - PHP server
php artisan serve

# In another terminal - Vite dev server
npm run dev

Visit http://localhost:8000 to see your application.

Creating Your First Module

The Core PHP Framework uses a modular architecture. Features are organised as self-contained modules.

Using the Artisan Command

# Create a full-featured module
php artisan make:mod Blog --all

# Or select specific features
php artisan make:mod Blog --web --api

Manual Creation

  1. Create the module directory:
mkdir -p app/Mod/Blog/{Models,Routes,Views,Livewire,Migrations,Tests}
  1. Create app/Mod/Blog/Boot.php:
<?php

declare(strict_types=1);

namespace App\Mod\Blog;

use Core\Events\WebRoutesRegistering;

class Boot
{
    public static array $listens = [
        WebRoutesRegistering::class => 'onWebRoutes',
    ];

    public function onWebRoutes(WebRoutesRegistering $event): void
    {
        $event->routes(fn() => require __DIR__.'/Routes/web.php');
        $event->views('blog', __DIR__.'/Views');
    }
}
  1. Create app/Mod/Blog/Routes/web.php:
<?php

use Illuminate\Support\Facades\Route;

Route::get('/blog', function () {
    return view('blog::index');
})->name('blog.index');
  1. Create app/Mod/Blog/Views/index.blade.php:
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Blog</title>
</head>
<body>
    <h1>Welcome to the Blog</h1>
</body>
</html>

Visit http://localhost:8000/blog to see your module in action.

Adding a Model

Create app/Mod/Blog/Models/Post.php:

<?php

declare(strict_types=1);

namespace App\Mod\Blog\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;

class Post extends Model
{
    use HasFactory;

    protected $fillable = [
        'title',
        'slug',
        'content',
        'published_at',
    ];

    protected function casts(): array
    {
        return [
            'published_at' => 'datetime',
        ];
    }
}

Create a migration in app/Mod/Blog/Migrations/:

<?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('posts', function (Blueprint $table) {
            $table->id();
            $table->string('title');
            $table->string('slug')->unique();
            $table->text('content');
            $table->timestamp('published_at')->nullable();
            $table->timestamps();
        });
    }

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

Run the migration:

php artisan migrate

Adding a Livewire Component

Create app/Mod/Blog/Livewire/PostListPage.php:

<?php

declare(strict_types=1);

namespace App\Mod\Blog\Livewire;

use App\Mod\Blog\Models\Post;
use Illuminate\Contracts\View\View;
use Livewire\Component;
use Livewire\WithPagination;

class PostListPage extends Component
{
    use WithPagination;

    public function render(): View
    {
        return view('blog::posts.index', [
            'posts' => Post::latest()->paginate(10),
        ]);
    }
}

Update app/Mod/Blog/Routes/web.php:

<?php

use App\Mod\Blog\Livewire\PostListPage;
use Illuminate\Support\Facades\Route;

Route::get('/blog', PostListPage::class)->name('blog.index');

Create app/Mod/Blog/Views/posts/index.blade.php:

<div>
    <h1 class="text-2xl font-bold mb-4">Blog Posts</h1>

    @forelse ($posts as $post)
        <article class="mb-4 p-4 border rounded">
            <h2 class="text-xl font-semibold">{{ $post->title }}</h2>
            <p class="text-gray-600">{{ Str::limit($post->content, 200) }}</p>
        </article>
    @empty
        <p>No posts yet.</p>
    @endforelse

    {{ $posts->links() }}
</div>

Writing Tests

Create app/Mod/Blog/Tests/PostTest.php:

<?php

use App\Mod\Blog\Models\Post;

it('displays the blog index', function () {
    $this->get('/blog')
        ->assertOk()
        ->assertSee('Blog Posts');
});

it('shows posts on the index page', function () {
    $post = Post::create([
        'title' => 'Test Post',
        'slug' => 'test-post',
        'content' => 'This is a test post.',
    ]);

    $this->get('/blog')
        ->assertOk()
        ->assertSee('Test Post');
});

Run tests:

vendor/bin/pest

Code Formatting

Before committing, run Laravel Pint:

# Format changed files only
vendor/bin/pint --dirty

# Format all files
vendor/bin/pint

Next Steps

Common Commands

# Development
php artisan serve              # Start PHP server
npm run dev                    # Start Vite with HMR
npm run build                  # Build for production

# Modules
php artisan make:mod Name      # Create a new module
php artisan make:mod Name --all # With all features

# Database
php artisan migrate            # Run migrations
php artisan migrate:fresh      # Reset and re-run migrations
php artisan db:seed            # Run seeders

# Testing
vendor/bin/pest                # Run all tests
vendor/bin/pest --filter=Name  # Run specific test

# Code Quality
vendor/bin/pint                # Format code
vendor/bin/pint --test         # Check formatting without changes