Weave Code
Code Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Api Crudify Laravel Package

mehedi8gb/api-crudify

Laravel package that generates standardized API CRUD (controller/service/repository/model/requests/resources/tests) and powers query-driven endpoints via a chainable pipeline for relations, filtering, sorting, soft deletes, and pagination—all controlled by URL query params.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Steps to First Use

  1. Install the Package

    composer require mehedi8gb/api-crudify --dev
    php artisan crudify:install
    
  2. Generate a CRUD Resource

    php artisan crudify:make Post
    

    This creates a full stack: Controller, Service, Repository, Model, Requests, Resource, Migration, Factory, Seeder, and Test.

  3. Define API Routes The package auto-registers routes in routes/api.php. Verify the generated routes:

    // routes/api.php
    Route::apiResource('posts', \App\Http\Controllers\PostController::class);
    
  4. Test the API

    php artisan serve
    

    Send a request to GET /api/posts with optional query params:

    /api/posts?q=title=laravel&limit=5
    

First Use Case: Quick Filtering

Use the ?q= shorthand to filter records:

GET /api/posts?q=title=laravel|status=published

This automatically applies:

  • Title contains "laravel"
  • Status equals "published" (OR logic)

Implementation Patterns

Core Workflow: Query-Driven API Development

  1. Define Model & Migration

    // app/Models/Post.php
    class Post extends Model
    {
        protected $with = ['author']; // Auto-load relations
        protected $fillable = ['title', 'content'];
    }
    
  2. Extend Repository for Custom Logic

    // app/Repositories/PostRepository.php
    class PostRepository extends BaseRepository
    {
        public function getPostsData(array $with = []): array
        {
            return $this->handleApiQueryRequest(
                $this->query()->where('published', true),
                $with
            );
        }
    }
    
  3. Customize Service Layer

    // app/Services/PostService.php
    class PostService extends BaseService
    {
        public function getPublishedPosts(): array
        {
            $data = $this->postRepository->getPostsData();
            return $this->prepareResourceResponse($data, PostResource::class);
        }
    }
    
  4. Override Controller Methods

    // app/Http/Controllers/PostController.php
    class PostController extends Controller
    {
        public function index(): JsonResponse
        {
            $posts = $this->postService->getPublishedPosts();
            return $this->successResponse('Posts retrieved', $posts);
        }
    }
    

Integration Tips

  • Domain-Specific Namespaces

    php artisan crudify:make V1/Blog/Post
    

    Generates routes under /api/v1/blog/posts.

  • Postman Schema Export

    php artisan crudify:make Post --export-api-schema
    

    Auto-generates OpenAPI schema in storage/api-schemas/.

  • Custom Query Handlers Extend AbstractQueryHandler for domain-specific logic:

    namespace App\Core\Query\Handlers\Custom;
    
    class PublishedOnlyHandler extends AbstractQueryHandler
    {
        public function handle($builder, $request): void
        {
            $builder->where('published', true);
        }
    }
    

    Register in BaseRepository::getQueryHandlers().

  • Bulk Operations Use handleApiBulkRequest() in repositories for batch operations:

    public function bulkUpdate(array $ids, array $data): array
    {
        return $this->handleApiBulkRequest($ids, $data, 'update');
    }
    
  • Caching Strategies Leverage cacheQuery() helper:

    $cachedData = cacheQuery(
        fn() => $this->getPostsData(),
        'getPosts',
        [],
        60 * 15 // 15 minutes
    );
    

Gotchas and Tips

Pitfalls

  1. Query Handler Order Matters The pipeline executes in this order: SoftDelete → Relations → Filter → Sort → Pagination Reordering handlers in BaseRepository::getQueryHandlers() can break expected behavior.

  2. Soft Delete Conflicts If ?trashed=with is used but the model lacks soft deletes, the query will fail. Ensure:

    use Illuminate\Database\Eloquent\SoftDeletes;
    class Post extends Model { use SoftDeletes; }
    
  3. Relation Loading Overrides Explicit $with in handleApiQueryRequest() overrides:

    • Model’s $with property
    • Eloquent’s getEagerLoads() Pass [] to use defaults.
  4. Pagination Edge Cases

    • ?limit=all bypasses pagination but still applies other handlers.
    • Pagination metadata (meta object) is omitted when limit=all.
  5. Validation Requests Generated StoreRequest/UpdateRequest use authorize() but not rules(). Extend them:

    public function rules(): array
    {
        return [
            'title' => 'required|string|max:255',
            'content' => 'required|string',
        ];
    }
    

Debugging Tips

  1. Inspect Query Pipeline Temporarily modify HandleApiQueryRequest to log each handler’s output:

    // app/Core/Query/HandleApiQueryRequest.php
    public function handleApiQueryRequest($builder, array $with = []): array
    {
        dd($this->queryHandlers); // Debug handler chain
        // ...
    }
    
  2. Validate Query Parameters Use tinker to test parameter parsing:

    php artisan tinker
    >>> $request = new \Illuminate\Http\Request(['q' => 'title=test']);
    >>> (new \App\Core\Query\Handlers\Core\FilterHandler())->handle($builder, $request);
    
  3. Check Handler Registration Verify handlers are loaded in BaseRepository:

    protected function getQueryHandlers(): array
    {
        return [
            new SoftDeleteHandler(),
            new RelationHandler(),
            new FilterHandler(),
            new SortHandler(),
            new PaginationHandler(),
        ];
    }
    

Extension Points

  1. Custom Response Formatting Override BaseService::prepareResourceResponse():

    protected function prepareResourceResponse($data, string $resourceClass): array
    {
        return [
            'data' => $this->resource($data, $resourceClass),
            'custom_meta' => ['total_items' => count($data)],
        ];
    }
    
  2. Dynamic Route Binding Extend BaseController to add custom route model binding:

    protected function getRouteKeyName(): string
    {
        return 'slug'; // Override default 'id'
    }
    
  3. Query Cache Invalidation Use cache()->forget() in service methods:

    public function updatePost(Post $post, array $data): Post
    {
        $post->update($data);
        cache()->forget('posts_list'); // Invalidate cache
        return $post;
    }
    
  4. API Versioning For multi-version APIs, extend BaseService:

    class V2\PostService extends BaseService
    {
        protected $version = 'v2';
        // Custom logic for v2
    }
    

Configuration Quirks

  1. Autoloading After crudify:install, run:

    composer dump-autoload
    

    If helpers aren’t available.

  2. Base Class Restoration The package auto-restores missing base classes on crudify:make. To bypass:

php artisan crudify:make Post --skip-restore
  1. Route Prefix Modify config/crudify.php to change the default /api prefix:

    'prefix' => 'app',
    
  2. Pagination Defaults Override in BaseRepository:

    protected function getDefaultPagination(): array
    {
        return ['limit' => 20, 'page' => 1];
    }
    

Performance Tips

  1. Selective Field Loading Use ->select() in custom queries to reduce payload size:

    $builder->select(['id', 'title', 'created_at']);
    
  2. Disable Pipeline for Simple Queries Bypass handlers with raw queries:

    $this->model::where('status', 'active')->get();
    
  3. Cache Query Handlers Cache frequent queries at the repository level:

    public function getFeaturedPosts(): array
    {
        return cache()->remember('featured_posts', 3600, function() {
            return $this->handleApiQueryRequest(
                $this->query()->where('featured', true)->limit(10)
            );
        });
    }
    
  4. Lazy-Load Relations Use `with

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
nexmo/api-specification
capell-app/block-library
axium/identity
cetria/laravel-dummy-models
cetria/reflection-helper
agropredict/sso-auth-bundle
evolvestudio/spam-protection
datacore/hub-sdk
develia/commons
cuci/prototurk-sdk
cuci/prototurk-sdk-symfony
develia/geo-bundle
dreamzy/livewire-charts
touchestate-sdk/php-sdk
ecotone/kafka
22h/doctrine-garbage-collection-bundle
agtp/agtp-php
agtp/mod-php
splash/sonata-admin
splash/metadata