ale95m/easy

API development package with laravel

Installs: 152

Dependents: 0

Suggesters: 0

Security: 0

Stars: 2

Watchers: 2

Forks: 0

Open Issues: 0

pkg:composer/ale95m/easy

4.0.0 2025-10-14 23:59 UTC

README

Speed up your Laravel API development with repositories, controllers, advanced filtering, and automatic logging.

Tests Documentation PHP Version Laravel

Features

  • Repository Pattern - Pre-built base repository with CRUD operations
  • Advanced Filtering - Multiple operators (=, like, >, <, >=, <=, null) and relation filtering
  • RESTful Controllers - Ready-to-use controller with all REST methods
  • Flexible Authentication - Driver-based system supporting Passport, Sanctum, JWT, and Session
  • Automatic Logging - Track all model changes with user and IP tracking
  • Code Generators - Artisan commands to generate repositories, controllers, models, and seeders
  • Soft Deletes Support - Built-in soft delete handling
  • Excel Export - Export data to Excel with custom formatting
  • Well Tested - 80+ tests ensuring reliability

Table of Contents

Installation

Requirements

  • PHP >= 8.2
  • Laravel 10.x, 11.x, or 12.x

Install via Composer

composer require ale95m/laravel-easy

Run Migrations

Easy includes migrations for logs and files tables:

php artisan migrate

Publish Configuration (Optional)

php artisan vendor:publish --tag=easy-config

Quick Start

Create a complete CRUD in 5 minutes:

1. Create Everything at Once

# Create model with migration, repository, controller, and seeder
php artisan easy:model Post -m -r -c -s

# Or use the shorthand --easy flag
php artisan easy:model Post --easy

This single command creates:

  • app/Models/Post.php - Model
  • database/migrations/*_create_posts_table.php - Migration
  • app/Repositories/PostRepository.php - Repository
  • app/Http/Controllers/PostController.php - Controller
  • database/seeders/PostSeeder.php - Seeder

2. Define Migration

Schema::create('posts', function (Blueprint $table) {
    $table->id();
    $table->string('title');
    $table->text('content');
    $table->string('status')->default('draft');
    $table->foreignId('user_id')->constrained();
    $table->timestamps();
    $table->softDeletes();
});

Run migration:

php artisan migrate

3. Configure Repository

Edit app/Repositories/PostRepository.php to add filters:

<?php

namespace App\Repositories;

use App\Models\Post;
use Easy\Repositories\EasyRepository;
use Illuminate\Database\Eloquent\Model;

class PostRepository extends EasyRepository
{
    protected array $filters = [
        'title:like',
        'status',
        'user_id',
        'created_at:>',
        'created_at:<',
    ];
    
    protected array $sortable_fields = ['title', 'created_at'];
    protected ?string $orderBy = 'created_at';
    protected bool $orderByAsc = false;
    protected bool $allow_load_deleted = true;

    public function getModel(): Model
    {
        return new Post();
    }
}

4. Add Validation (Optional)

Edit app/Http/Controllers/PostController.php to add validation:

public function store(Request $request)
{
    $request->validate([
        'title' => 'required|string|max:255',
        'content' => 'required',
        'status' => 'in:draft,published',
        'user_id' => 'required|exists:users,id',
    ]);

    return $this->baseStore($request);
}

5. Add Routes

In routes/api.php:

use App\Http\Controllers\PostController;

Route::middleware('auth:sanctum')->group(function () {
    Route::apiResource('posts', PostController::class);
    Route::post('posts/{id}/restore', [PostController::class, 'restore']);
});

6. Test Your API!

# List all posts
GET /api/posts

# Create a post
POST /api/posts
{
  "title": "My First Post",
  "content": "Hello World!",
  "status": "published",
  "user_id": 1
}

# Get a single post
GET /api/posts/1

# Update a post
PUT /api/posts/1
{
  "title": "Updated Title"
}

# Delete a post (soft delete)
DELETE /api/posts/1

# Restore a deleted post
POST /api/posts/1/restore

# Filter by status
GET /api/posts?status=published

# Search by title
GET /api/posts?title:like=%Laravel%

# Sort results
GET /api/posts?sort_by=title&sort_asc=true

# Include soft-deleted
GET /api/posts?with_deleted=true

Core Concepts

Easy Repository

The EasyRepository is an abstract base class that implements the Repository Pattern with powerful features.

Basic Usage

<?php

namespace App\Repositories;

use App\Models\User;
use Easy\Repositories\EasyRepository;
use Illuminate\Database\Eloquent\Model;

class UserRepository extends EasyRepository
{
    protected array $filters = [
        'name:like',
        'email:like',
        'status',
        'age:>',
        'age:<',
    ];

    protected array $sortable_fields = ['name', 'email', 'created_at'];
    protected ?string $orderBy = 'created_at';
    protected bool $orderByAsc = false;

    public function getModel(): Model
    {
        return new User();
    }
}

Available Methods

// Create
$user = $repository->create($data, $log = true);

// Find
$user = $repository->findOrFail($id);

// Update
$repository->update($user, $data, $log = true);

// Delete (soft delete)
$repository->delete($user, $log = true);

// Restore
$repository->restore($id);

// Search with filters
$query = $repository->search([
    'name:like' => '%John%',
    'age:>' => 18,
    'status' => 'active',
]);
$results = $query->get();

Configuration Properties

protected array $filters = [];              // Filterable fields
protected array $filters_alias = [];        // Filter aliases
protected array $sortable_fields = [];      // Sortable fields
protected ?string $orderBy = null;          // Default sort field
protected bool $orderByAsc = true;          // Default sort direction
protected ?array $relationships = null;     // Auto-load relationships
protected array $select_fields = ['*'];     // Fields to select
protected bool $allow_load_deleted = false; // Enable soft delete filtering
protected bool $use_uuid = false;           // Use UUID as primary key
protected array $checkDelete = [];          // Relations to check before delete

Helper Hooks

Override these methods to add custom logic:

protected function creating(array &$data): void
{
    // Runs before create
    $data['created_by'] = auth()->id();
}

protected function created(Model &$model, array &$data): void
{
    // Runs after create
}

protected function updating(Model &$model, array &$data): void
{
    // Runs before update
}

protected function updated(Model &$model, array &$data): void
{
    // Runs after update
}

protected function deleting(Model &$model): void
{
    // Runs before delete
}

protected function deleted(Model &$model): void
{
    // Runs after delete
}

Easy Controller

The EasyController provides ready-to-use RESTful endpoints.

Basic Usage

<?php

namespace App\Http\Controllers;

use App\Repositories\UserRepository;
use Easy\Http\Controllers\EasyController;

class UserController extends EasyController
{
    protected array $uniqueFields = ['email'];
    protected bool $use_logs = true;

    public function __construct()
    {
        $this->repository = new UserRepository();
    }
}

Available Methods

All these methods are inherited from EasyController:

  • index(Request $request) - List all resources with filtering
  • store(Request $request) - Create a new resource
  • show($id) - Get a single resource
  • update(Request $request, $id) - Update a resource
  • destroy($id) - Delete a resource (soft delete)
  • restore($id) - Restore a soft-deleted resource
  • paginate(PaginateRequest $request) - Get paginated results

Custom Validation

public function store(Request $request): JsonResponse
{
    $request->validate([
        'name' => 'required|string|max:255',
        'email' => 'required|email|unique:users',
    ]);

    return $this->baseStore($request);
}

Filtering System

Easy provides a powerful filtering system with multiple operators.

Filter Operators

protected array $filters = [
    'name',           // Exact match: ?name=John
    'name:like',      // LIKE operator: ?name:like=%John%
    'email:like',     // LIKE: ?email:like=%@example.com
    'age:>',          // Greater than: ?age:>=18
    'age:<',          // Less than: ?age:<65
    'age:>=',         // Greater or equal: ?age:>=18
    'age:<=',         // Less or equal: ?age:<=65
    'status:null',    // IS NULL / IS NOT NULL: ?status:null=true
];

Relation Filtering

Filter by relationship fields using the -> syntax:

protected array $filters = [
    'posts->status',           // ?posts->status=published
    'posts->title:like',       // ?posts->title:like=%Laravel%
    'posts->views:>',          // ?posts->views:>1000
    'profile->country',        // ?profile->country=USA
    'posts->comments->status', // Nested: ?posts->comments->status=approved
];

Filter Aliases

Create friendly names for filters:

protected array $filters_alias = [
    'username' => 'name',
    'user_email' => 'email',
];

// Usage: ?username=john (maps to name)

Custom Search Methods

Create custom filter logic:

// In repository
protected array $filters = ['active_users'];

public function searchByActiveUsers($query, $value)
{
    if ($value) {
        $query->where('status', 'active')
              ->whereNotNull('email_verified_at');
    }
}

// Usage: ?active_users=true

Usage Examples

// Simple filter
$users = $repository->search(['status' => 'active'])->get();

// Multiple filters
$users = $repository->search([
    'status' => 'active',
    'age:>' => 18,
    'name:like' => '%John%',
])->get();

// Filter by relations
$users = $repository->search([
    'posts->status' => 'published',
    'posts->views:>' => 1000,
])->get();

// With sorting
$users = $repository->search([
    'status' => 'active',
    'sort_by' => 'name',
    'sort_asc' => true,
])->get();

// With soft deletes
$users = $repository->search([
    'with_deleted' => true
])->get();

Complete filtering documentation: docs/filtering-system.md

Authentication System

Easy provides a flexible, driver-based authentication system that works with multiple authentication backends without code changes.

Supported Drivers

  • 🔐 Passport - OAuth2 server (laravel/passport)
  • 🎫 Sanctum - Simple API tokens (laravel/sanctum)
  • 🔑 JWT - JSON Web Tokens (tymon/jwt-auth)
  • 📝 Session - Traditional Laravel auth (built-in)
  • 🔧 Custom - Build your own driver

Quick Setup

  1. Choose your driver in .env:
EASY_AUTH_DRIVER=sanctum  # passport|sanctum|jwt|session
  1. Install driver package (if needed):
# For Sanctum
composer require laravel/sanctum
php artisan vendor:publish --provider="Laravel\Sanctum\SanctumServiceProvider"
php artisan migrate
  1. Update your User model:
use Easy\Interfaces\IAuthenticableWithToken;
use Easy\Traits\HasTokenAuthentication;
use Laravel\Sanctum\HasApiTokens; // For Sanctum

class User extends Authenticatable implements IAuthenticableWithToken
{
    use HasApiTokens, HasTokenAuthentication;

    // Optional: allow login with email OR username
    protected $authField = ['email', 'username'];
}

Usage

Login:

POST /api/login
{
  "email": "user@example.com",
  "password": "secret"
}

# Response includes token
{
  "status": true,
  "data": {...},
  "token": "eyJ0eXAiOiJKV1QiLCJhbGc..."
}

In your code:

// Get token
$token = $user->getToken();

// Revoke token
$user->revokeToken();

// Get current driver
$driver = $user->getTokenDriver(); // 'sanctum', 'passport', etc.

Direct TokenDriverManager usage:

use Easy\Auth\TokenDriverManager;

$token = TokenDriverManager::createToken($user);
TokenDriverManager::revokeToken($user);

Create Custom Driver

namespace App\Auth;

use Easy\Auth\Contracts\TokenDriverInterface;

class MyCustomDriver implements TokenDriverInterface
{
    public function createToken(Model $user, ?string $tokenName = null): string
    {
        // Your logic
        return 'custom-token';
    }
    // ... implement other methods
}

// Register in config/easy.php
'auth' => [
    'driver' => 'my-custom',
    'drivers' => [
        'my-custom' => \App\Auth\MyCustomDriver::class,
    ],
]

Migration from Old System

The old IAuthenticableOAuth (Passport-specific) is still supported but deprecated. Migrate to the new system:

// Before (Passport only)
use Easy\Traits\IsAuthenticableOAuth;

// After (Works with any driver)
use Easy\Traits\HasTokenAuthentication;
use Laravel\Sanctum\HasApiTokens; // or Passport's HasApiTokens

class User extends Authenticatable implements IAuthenticableWithToken
{
    use HasApiTokens, HasTokenAuthentication;
}

Complete authentication documentation: AUTHENTICATION.md | docs/authentication.md

Logging System

Easy includes automatic logging to track all model changes.

Make Your Model Loggable

<?php

namespace App\Models;

use Easy\Interfaces\ILogable;
use Easy\Traits\HasLogs;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;

class Post extends Model implements ILogable
{
    use SoftDeletes, HasLogs;
    
    protected $fillable = ['title', 'content', 'status'];
    
    // Define which attributes should be logged
    protected $logableAttributes = ['title', 'content', 'status'];
}

Enable Logging in Operations

// In controller or repository
$post = $repository->create($data, true);        // true = create log
$repository->update($post, $changes, true);      // true = create log
$repository->delete($post, true);                // true = create log

Log Structure

Each log entry contains:

{
  "id": 1,
  "action": "create|update|delete|login|logout",
  "model": "post",
  "attributes": "{json}",
  "changes": "{json}",
  "ip": "192.168.1.1",
  "user_id": 1,
  "logable_type": "App\\Models\\Post",
  "logable_id": 5,
  "created_at": "2024-01-15 10:30:00"
}

Retrieve Logs

// Get logs for a model
$logs = $post->logs()->get();

// Get logs with filters
use Easy\Repositories\LogRepository;

$logRepository = new LogRepository();
$logs = $logRepository->search([
    'model:like' => '%Post%',
    'action' => 'update',
    'created_at:>' => '2024-01-01'
])->get();

Manual Logging

use Easy\Repositories\LogRepository;

// Log login
LogRepository::logInAction();

// Log logout
LogRepository::logOutAction();

// Custom log
LogRepository::createLog('custom_action', 'User', $attributes);

Artisan Commands

Easy provides powerful commands to generate code quickly.

💡 Tip: For complete command reference with advanced examples, see COMMANDS.md

Generate Repository

# Basic
php artisan easy:repository UserRepository

# With model
php artisan easy:repository UserRepository --model=User

# Multiple
php artisan easy:repository UserRepository PostRepository CommentRepository

Generate Controller

# Basic
php artisan easy:controller UserController

# With repository
php artisan easy:controller UserController --repository=UserRepository

# Multiple
php artisan easy:controller UserController PostController

Generate Model

The most powerful command - create models with all related files.

# Individual options
php artisan easy:model Post -m  # With migration
php artisan easy:model Post -r  # With repository
php artisan easy:model Post -c  # With controller (creates repository too)
php artisan easy:model Post -s  # With seeder (requires repository)

# Combine options freely
php artisan easy:model Post -m -r          # Model + Migration + Repository
php artisan easy:model Post -m -r -c       # Model + Migration + Repository + Controller
php artisan easy:model Post -m -r -c -s    # Everything

# Easy Mode (equivalent to -m -r -c -s)
php artisan easy:model Post --easy

# Create multiple models at once with any combination
php artisan easy:model Post Comment Tag -m -r -c -s
php artisan easy:model Product Category Brand --easy
php artisan easy:model User Post Comment -m -r

# Real-world example - complete blog structure
php artisan easy:model Post Category Tag Comment -m -r -c -s
# This creates for EACH model:
# - Model file (Post.php, Category.php, Tag.php, Comment.php)
# - Migration (create_posts_table, create_categories_table, etc.)
# - Repository (PostRepository, CategoryRepository, etc.)
# - Controller (PostController, CategoryController, etc.)
# - Seeder (PostSeeder, CategorySeeder, etc.)

Options:

  • -m, --migration - Create migration
  • -r, --repository - Create repository
  • -c, --controller - Create controller (automatically creates repository if needed)
  • -s, --seeder - Create seeder (automatically creates repository if needed)
  • -e, --easy - Create all of the above (equivalent to -m -r -c -s)

Generate Seeder

php artisan easy:seeder UserSeeder --repository=UserRepository

Interactive Create

php artisan easy:create

This interactive command will guide you through creating a complete CRUD.

Configuration

After publishing the configuration (php artisan vendor:publish --tag=easy-config), you'll find config/easy.php:

<?php

return [
    // Authentication
    'use_auth' => true,
    'api_prefix' => 'api',
    'api_middleware' => [],
    'auth_middleware' => ['auth'],
    
    // Files
    'files' => [
        'path' => 'files',
        'disk' => 'local',
    ],
    
    // Tables
    'tables' => [
        'files' => 'easy_files',
        'logs' => 'easy_logs',
    ],
    
    // Pagination
    'pagination' => [
        'input' => [
            'items_per_page' => 'itemsPerPage',
            'current_page' => 'page',
        ],
        'output' => [
            'items_per_page' => 'itemsPerPage',
            'current_page' => 'page',
            'items_length' => 'itemsLength',
            'page_count' => 'pageCount',
        ]
    ],
    
    // Query parameters
    'query' => [
        'sort_by' => 'sort_by',
        'sort_asc' => 'sort_asc',
        'only_deleted' => 'only_deleted',
        'with_deleted' => 'with_deleted',
    ],
    
    // Project directories
    'project_directories' => [
        'models' => 'App\\Models',
        'controllers' => 'App\\Http\\Controllers',
        'repositories' => 'App\\Repositories',
        'seeders' => 'Database\\Seeders',
    ],
];

Testing

Easy includes a comprehensive test suite with 75+ tests.

Run Tests

# Install dependencies
composer install

# Run all tests
composer test

# Run with test documentation
vendor/bin/phpunit --testdox

# Run specific test suite
vendor/bin/phpunit --testsuite=Feature

# With coverage
composer test-coverage

Test Coverage

  • ✅ Artisan commands (repository, controller, seeder generation)
  • ✅ Repository CRUD operations
  • ✅ Advanced filtering (all operators, relations, aliases)
  • ✅ Controller REST methods
  • ✅ Logging system (create, update, delete logs)
  • ✅ Soft deletes
  • ✅ Custom search methods

For more information, see TESTING.md

Examples

Blog System with Relations

// Post Model
class Post extends Model implements ILogable
{
    use SoftDeletes, HasLogs;

    protected $fillable = ['title', 'content', 'status', 'user_id', 'category_id'];
    protected $logableAttributes = ['title', 'content', 'status'];

    public function user() { return $this->belongsTo(User::class); }
    public function category() { return $this->belongsTo(Category::class); }
    public function tags() { return $this->belongsToMany(Tag::class); }
}

// Post Repository
class PostRepository extends EasyRepository
{
    protected array $filters = [
        'title:like',
        'status',
        'user_id',
        'category_id',
        'created_at:>',
        'created_at:<',
        'user->name:like',      // Filter by author name
        'category->name',        // Filter by category
        'tags->name',            // Filter by tag
    ];

    protected ?array $relationships = ['user:id,name', 'category:id,name', 'tags'];
    protected bool $allow_load_deleted = true;

    public function getModel(): Model { return new Post(); }
    
    // Custom search
    public function searchByPopular($query, $value)
    {
        if ($value) {
            $query->where('views', '>', 1000)->orderBy('views', 'desc');
        }
    }
}

// Usage Examples
// Filter by author
GET /api/posts?user->name:like=%John%

// Filter by category
GET /api/posts?category->name=Technology

// Filter by tag
GET /api/posts?tags->name=Laravel

// Get popular posts
GET /api/posts?popular=true

// Combined filters
GET /api/posts?status=published&category->name=Tech&created_at:>2024-01-01

E-commerce Product Management

// Product Repository
class ProductRepository extends EasyRepository
{
    protected array $filters = [
        'name:like',
        'sku',
        'is_active',
        'price:>',
        'price:<',
        'stock:>',
        'stock:<',
        'category->name',
    ];

    protected array $sortable_fields = ['name', 'price', 'stock', 'created_at'];

    public function getModel(): Model { return new Product(); }

    public function searchByInStock($query, $value)
    {
        if ($value) {
            $query->where('stock', '>', 0)->where('is_active', true);
        }
    }

    public function searchByLowStock($query, $value)
    {
        if ($value) {
            $query->where('stock', '>', 0)->where('stock', '<', 10);
        }
    }
}

// Usage
GET /api/products?in_stock=true
GET /api/products?low_stock=true
GET /api/products?price:>=10&price:<=100
GET /api/products?category->name=Electronics

User Management with Custom Validation

// User Controller
class UserController extends EasyController
{
    protected array $uniqueFields = ['email'];
    protected bool $use_logs = true;

    public function __construct()
    {
        $this->repository = new UserRepository();
    }

    public function store(Request $request): JsonResponse
    {
        $request->validate([
            'name' => 'required|string|max:255',
            'email' => 'required|email|unique:users',
            'password' => 'required|min:8',
        ]);

        $request->merge([
            'password' => Hash::make($request->password)
        ]);

        return $this->baseStore($request);
    }

    public function update(Request $request, $id): JsonResponse
    {
        $request->validate([
            'email' => 'sometimes|email|unique:users,email,'.$id,
            'password' => 'sometimes|min:8',
        ]);

        if ($request->has('password')) {
            $request->merge([
                'password' => Hash::make($request->password)
            ]);
        }

        return $this->baseUpdate($request, $id);
    }
}

Advanced Features

Excel Export

If your repository uses the ExportableToExcel trait:

use Easy\Traits\ExportableToExcel;

class UserRepository extends EasyRepository
{
    use ExportableToExcel;
    
    // ... your repository code
}

// In controller
use Easy\Http\Requests\ExportExcelRequest;

public function export(ExportExcelRequest $request)
{
    return $this->repository->exportExcel($request->all());
}

Authentication

Easy includes authentication traits:

use Easy\Interfaces\IAuthenticable;
use Easy\Traits\IsAuthenticable;

class User extends Model implements IAuthenticable
{
    use IsAuthenticable;
    
    // ... your model code
}

File Management

Easy includes a file management system with the FileController and FileRepository.

Best Practices

  1. Always use filter definitions - Only allow filtering on indexed fields
  2. Enable logging on important operations - Track changes to sensitive data
  3. Use soft deletes - Maintain data integrity and audit trail
  4. Validate unique fields - Prevent duplicates at controller level
  5. Eager load relationships - Prevent N+1 queries
  6. Use custom search methods - Keep complex logic in repositories
  7. Index filtered fields - Add database indexes for performance
  8. Paginate large datasets - Always paginate in production

Documentation

📚 Full Documentation - Complete documentation with examples, guides, and API reference

Documentation Files

Quick Links

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

Development Setup

git clone https://github.com/ale95m/laravel-easy.git
cd laravel-easy
composer install
composer test

License

The Easy package is open-sourced software licensed under the MIT license.

Credits

Support

Made with ❤️ for the Laravel community