anas/easy-dev
Interactive Laravel code generator for complete CRUD with repository/service patterns. Auto-detects model relationships and scaffolds policies, DTOs, observers, filters, enums, API resources, routes, and more, with dry-run mode and customizable stubs.
Laravel Easy Dev v2 is a powerful, feature-rich package that revolutionizes Laravel development by providing:
composer require anas/easy-dev
php artisan vendor:publish --tag=easy-dev-config
php artisan vendor:publish --tag=easy-dev-stubs
php artisan easy-dev:help
You should see a beautiful help interface confirming successful installation.
php artisan make:model Product -m
// database/migrations/xxxx_create_products_table.php
Schema::create('products', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->text('description')->nullable();
$table->decimal('price', 8, 2);
$table->integer('stock')->default(0);
$table->foreignId('category_id')->constrained();
$table->boolean('is_active')->default(true);
$table->timestamps();
});
php artisan migrate
php artisan easy-dev:make Product
php artisan easy-dev:sync-relations --all
That's it! You now have a complete CRUD system with:
easy-dev:make {model}Enhanced CRUD generator with beautiful interactive UI.
# Basic usage
php artisan easy-dev:make Product
# With all options
php artisan easy-dev:make Product --with-repository --with-service --api-only --interactive
Options:
--with-repository: Include Repository pattern--with-service: Include Service layer--without-interface: Skip interface generation--api-only: Generate API controller only--web-only: Generate web controller only--interactive: Run in interactive modeeasy-dev:crud {model}Classic CRUD generator with Repository and Service patterns.
# Generate complete CRUD with patterns
php artisan easy-dev:crud Product --with-repository --with-service
# API-only CRUD
php artisan easy-dev:crud Product --api-only
easy-dev:repository {model}Generate repository pattern files for existing models.
# Generate repository with interface
php artisan easy-dev:repository Product
# Repository without interface
php artisan easy-dev:repository Product --without-interface
easy-dev:api-resource {model}Generate API resource and collection classes.
php artisan easy-dev:api-resource Product
easy-dev:sync-relations {model?}Automatically detect and add relationships to models.
# Sync relations for specific model
php artisan easy-dev:sync-relations Product
# Sync relations for all models
php artisan easy-dev:sync-relations --all
# With polymorphic targets
php artisan easy-dev:sync-relations Comment --morph-targets=Post,Video,Image
easy-dev:add-relation {model} {type} {related}Manually add specific relationships.
# Add belongsTo relationship
php artisan easy-dev:add-relation Post belongsTo User
# Add hasMany with custom method name
php artisan easy-dev:add-relation User hasMany Post --method=articles
# Add belongsToMany relationship
php artisan easy-dev:add-relation User belongsToMany Role
easy-dev:helpDisplay beautiful help guide with all available options.
php artisan easy-dev:help
easy-dev:demo-uiDemonstrate the package's beautiful UI capabilities.
php artisan easy-dev:demo-ui
The interactive mode provides a guided, step-by-step experience for generating CRUD operations.
php artisan easy-dev:make Product --interactive
Laravel Easy Dev v2 features intelligent relationship detection that analyzes your database schema and automatically generates appropriate Eloquent relationships.
Detected from: Foreign key columns (e.g., user_id, category_id)
// Automatically generated in Post model
public function user()
{
return $this->belongsTo(User::class);
}
public function category()
{
return $this->belongsTo(Category::class);
}
Detected from: Reverse foreign key relationships
// Automatically generated in User model
public function posts()
{
return $this->hasMany(Post::class);
}
// Automatically generated in Category model
public function products()
{
return $this->hasMany(Product::class);
}
Detected from: Pivot tables (tables with multiple foreign keys)
// Automatically generated when post_tag table exists
public function tags()
{
return $this->belongsToMany(Tag::class);
}
Detected from: _type and _id column patterns
// Morphable columns: commentable_type, commentable_id
public function commentable()
{
return $this->morphTo();
}
// Reverse relationship
public function comments()
{
return $this->morphMany(Comment::class, 'commentable');
}
Detected from: parent_id columns
// Parent/child relationships
public function parent()
{
return $this->belongsTo(Category::class, 'parent_id');
}
public function children()
{
return $this->hasMany(Category::class, 'parent_id');
}
-- Categories table
CREATE TABLE categories (
id BIGINT PRIMARY KEY,
name VARCHAR(255),
parent_id BIGINT NULL,
FOREIGN KEY (parent_id) REFERENCES categories(id)
);
-- Products table
CREATE TABLE products (
id BIGINT PRIMARY KEY,
name VARCHAR(255),
category_id BIGINT,
user_id BIGINT,
FOREIGN KEY (category_id) REFERENCES categories(id),
FOREIGN KEY (user_id) REFERENCES users(id)
);
-- Product reviews (polymorphic)
CREATE TABLE reviews (
id BIGINT PRIMARY KEY,
content TEXT,
reviewable_type VARCHAR(255),
reviewable_id BIGINT,
user_id BIGINT,
FOREIGN KEY (user_id) REFERENCES users(id)
);
-- Product tags (many-to-many)
CREATE TABLE product_tag (
product_id BIGINT,
tag_id BIGINT,
FOREIGN KEY (product_id) REFERENCES products(id),
FOREIGN KEY (tag_id) REFERENCES tags(id)
);
Running php artisan easy-dev:sync-relations --all will generate:
Category Model:
// Self-referencing
public function parent()
{
return $this->belongsTo(Category::class, 'parent_id');
}
public function children()
{
return $this->hasMany(Category::class, 'parent_id');
}
// Has many products
public function products()
{
return $this->hasMany(Product::class);
}
Product Model:
// Belongs to relationships
public function category()
{
return $this->belongsTo(Category::class);
}
public function user()
{
return $this->belongsTo(User::class);
}
// Many-to-many
public function tags()
{
return $this->belongsToMany(Tag::class);
}
// Polymorphic
public function reviews()
{
return $this->morphMany(Review::class, 'reviewable');
}
foreignId()->constrained('table')foreignId()->constrained()_idtable1_table2 format_type_idmorphable_type, commentable_type, etc.Laravel Easy Dev v2 implements clean architecture patterns to separate concerns and improve code maintainability.
interface ProductRepositoryInterface
{
public function all(): Collection;
public function find(int $id): ?Product;
public function create(array $data): Product;
public function update(int $id, array $data): Product;
public function delete(int $id): bool;
public function findBy(string $field, $value): Collection;
public function paginate(int $perPage = 15): LengthAwarePaginator;
}
class ProductRepository implements ProductRepositoryInterface
{
public function __construct(private Product $model) {}
public function all(): Collection
{
return $this->model->all();
}
public function find(int $id): ?Product
{
return $this->model->find($id);
}
public function create(array $data): Product
{
return $this->model->create($data);
}
// ... other methods
}
interface ProductServiceInterface
{
public function getAllProducts(): Collection;
public function getProductById(int $id): ?Product;
public function createProduct(array $data): Product;
public function updateProduct(int $id, array $data): Product;
public function deleteProduct(int $id): bool;
public function searchProducts(string $query): Collection;
}
class ProductService implements ProductServiceInterface
{
public function __construct(
private ProductRepositoryInterface $repository,
private CategoryService $categoryService
) {}
public function createProduct(array $data): Product
{
// Business logic here
if (isset($data['category_id'])) {
$category = $this->categoryService->getCategoryById($data['category_id']);
if (!$category || !$category->is_active) {
throw new InvalidArgumentException('Invalid category selected');
}
}
return $this->repository->create($data);
}
// ... other methods with business logic
}
// Generated in RepositoryServiceProvider
public function register(): void
{
$this->app->bind(ProductRepositoryInterface::class, ProductRepository::class);
$this->app->bind(ProductServiceInterface::class, ProductService::class);
}
class ProductController extends Controller
{
public function __construct(
private ProductServiceInterface $productService
) {}
public function index()
{
$products = $this->productService->getAllProducts();
return view('products.index', compact('products'));
}
public function store(StoreProductRequest $request)
{
$product = $this->productService->createProduct($request->validated());
return redirect()->route('products.show', $product);
}
}
Laravel Easy Dev v2 provides comprehensive API development features with automatic resource generation and API-specific controllers.
class ProductResource extends JsonResource
{
public function toArray($request): array
{
return [
'id' => $this->id,
'name' => $this->name,
'description' => $this->description,
'price' => $this->price,
'stock' => $this->stock,
'is_active' => $this->is_active,
'category' => new CategoryResource($this->whenLoaded('category')),
'created_at' => $this->created_at?->toISOString(),
'updated_at' => $this->updated_at?->toISOString(),
];
}
}
class ProductCollection extends ResourceCollection
{
public function toArray($request): array
{
return [
'data' => $this->collection,
'meta' => [
'total' => $this->total(),
'count' => $this->count(),
'per_page' => $this->perPage(),
'current_page' => $this->currentPage(),
'total_pages' => $this->lastPage(),
],
];
}
}
class ProductApiController extends Controller
{
public function __construct(
private ProductServiceInterface $productService
) {}
public function index(Request $request)
{
$products = $this->productService->getAllProducts();
return new ProductCollection($products);
}
public function show(Product $product)
{
return new ProductResource($product->load('category', 'tags'));
}
public function store(StoreProductRequest $request)
{
$product = $this->productService->createProduct($request->validated());
return new ProductResource($product);
}
public function update(UpdateProductRequest $request, Product $product)
{
$product = $this->productService->updateProduct($product->id, $request->validated());
return new ProductResource($product);
}
public function destroy(Product $product)
{
$this->productService->deleteProduct($product->id);
return response()->json(['message' => 'Product deleted successfully']);
}
}
// routes/api.php
Route::apiResource('products', ProductApiController::class);
// Generates the following routes:
// GET /api/products - index
// POST /api/products - store
// GET /api/products/{id} - show
// PUT /api/products/{id} - update
// DELETE /api/products/{id} - destroy
{
"data": [
{
"id": 1,
"name": "Gaming Laptop",
"description": "High-performance gaming laptop",
"price": "1299.99",
"stock": 15,
"is_active": true,
"category": {
"id": 2,
"name": "Electronics"
},
"created_at": "2025-01-15T10:30:00.000000Z",
"updated_at": "2025-01-15T10:30:00.000000Z"
}
],
"meta": {
"total": 50,
"count": 15,
"per_page": 15,
"current_page": 1,
"total_pages": 4
}
}
Laravel Easy Dev v2 automatically generates comprehensive test suites for your CRUD operations.
class ProductControllerTest extends TestCase
{
use RefreshDatabase;
protected function setUp(): void
{
parent::setUp();
$this->user = User::factory()->create();
}
public function test_can_view_products_index()
{
$products = Product::factory(3)->create();
$response = $this->actingAs($this->user)
->get(route('products.index'));
$response->assertOk()
->assertViewIs('products.index')
->assertViewHas('products');
}
public function test_can_create_product()
{
$category = Category::factory()->create();
$productData = [
'name' => 'Test Product',
'description' => 'Test Description',
'price' => 99.99,
'stock' => 10,
'category_id' => $category->id,
];
$response = $this->actingAs($this->user)
->post(route('products.store'), $productData);
$response->assertRedirect(route('products.show', Product::latest()->first()));
$this->assertDatabaseHas('products', $productData);
}
public function test_can_update_product()
{
$product = Product::factory()->create();
$updateData = ['name' => 'Updated Product Name'];
$response = $this->actingAs($this->user)
->put(route('products.update', $product), $updateData);
$response->assertRedirect(route('products.show', $product));
$this->assertDatabaseHas('products', $updateData);
}
public function test_can_delete_product()
{
$product = Product::factory()->create();
$response = $this->actingAs($this->user)
->delete(route('products.destroy', $product));
$response->assertRedirect(route('products.index'));
$this->assertDatabaseMissing('products', ['id' => $product->id]);
}
}
class ProductApiTest extends TestCase
{
use RefreshDatabase;
public function test_can_get_products_list()
{
Product::factory(3)->create();
$response = $this->getJson('/api/products');
$response->assertOk()
->assertJsonStructure([
'data' => [
'*' => [
'id',
'name',
'description',
'price',
'stock',
'is_active',
'created_at',
'updated_at'
]
],
'meta' => [
'total',
'count',
'per_page',
'current_page',
'total_pages'
]
]);
}
public function test_can_create_product_via_api()
{
$category = Category::factory()->create();
$productData = [
'name' => 'API Test Product',
'description' => 'Created via API',
'price' => 149.99,
'stock' => 25,
'category_id' => $category->id,
];
$response = $this->postJson('/api/products', $productData);
$response->assertCreated()
->assertJsonFragment($productData);
}
}
class ProductServiceTest extends TestCase
{
protected ProductService $productService;
protected MockInterface $repositoryMock;
protected function setUp(): void
{
parent::setUp();
$this->repositoryMock = Mockery::mock(ProductRepositoryInterface::class);
$this->app->instance(ProductRepositoryInterface::class, $this->repositoryMock);
$this->productService = app(ProductService::class);
}
public function test_can_create_product()
{
$productData = [
'name' => 'Test Product',
'price' => 99.99,
'stock' => 10,
];
$expectedProduct = new Product($productData);
$this->repositoryMock->shouldReceive('create')
->once()
->with($productData)
->andReturn($expectedProduct);
$result = $this->pr...
How can I help you explore Laravel packages today?