Installation
composer require antimattr/common-product
Publish the package config (if needed):
php artisan vendor:publish --provider="Antimattr\CommonProduct\CommonProductServiceProvider" --tag="config"
Basic Model Usage
Extend the provided Product model in your app/Models directory:
namespace App\Models;
use Antimattr\CommonProduct\Models\Product as CommonProduct;
class Product extends CommonProduct
{
// Customize as needed
}
First Use Case: CRUD Operations
use App\Models\Product;
// Create
$product = Product::create([
'name' => 'Premium Widget',
'sku' => 'WIDGET-PREM-001',
'price' => 19.99,
'description' => 'High-quality widget',
'stock' => 100
]);
// Retrieve
$product = Product::find(1);
// Update
$product->update(['price' => 17.99]);
// Delete
$product->delete();
Key Features to Explore
app/Providers/AppServiceProvider for default validation rules.active(), inStock(), or byPriceRange().// Update stock for multiple products
Product::whereIn('id', [1, 2, 3])->update(['stock' => function($query) {
return $query->decrement('stock', 5);
}]);
// Trigger stock validation before order processing
$product = Product::find($id);
if (!$product->validateStock($requiredQuantity)) {
throw new \Exception('Insufficient stock');
}
// Apply a discount to a product
$product->applyDiscount(10); // 10% discount
$product->save();
$priceWithTax = $product->calculatePriceWithTax($taxRate);
ProductResource:
namespace App\Http\Resources;
use Antimattr\CommonProduct\Http\Resources\ProductResource as CommonProductResource;
class ProductResource extends CommonProductResource
{
public function toArray($request)
{
$array = parent::toArray($request);
$array['custom_field'] = $this->custom_field;
return $array;
}
}
ProductCreated, StockUpdated):
// In EventServiceProvider
protected $listen = [
'Antimattr\CommonProduct\Events\ProductCreated' => [
'App\Listeners\SendProductCreatedNotification',
],
];
Database Migrations:
The package includes migrations for the core products table. Run:
php artisan migrate
If extending, create a new migration for custom fields and add them to the model.
API Routes:
Use the provided route macros (if any) or leverage the ProductController as a base:
Route::apiResource('products', \Antimattr\CommonProduct\Http\Controllers\ProductController::class);
Testing:
Use the ProductFactory for seeding test data:
use Antimattr\CommonProduct\Database\Factories\ProductFactory;
ProductFactory::new()->create();
Overriding Default Behavior:
boot(), ensure you call parent::boot() to retain default observers/scopes.protected static function boot()
{
parent::boot();
// Custom boot logic
}
Validation Rules:
AppServiceProvider. If you customize them, ensure the validate() method in your model still uses $this->rules().public function rules()
{
return array_merge(parent::rules(), [
'custom_field' => 'required|string',
]);
}
Stock Management:
StockObserver that may throw exceptions on negative stock. Disable or customize it if needed:
// In your Product model
protected static function boot()
{
static::observe(\Antimattr\CommonProduct\Observers\StockObserver::class);
// Or replace with your own observer
}
Soft Deletes:
SoftDeletes. Ensure your queries account for this:
// With soft deletes
$product = Product::withTrashed()->find($id);
// Without soft deletes (if needed)
$product = Product::withoutGlobalScopes()->find($id);
Caching:
php artisan cache:clear
Log Observers: Temporarily add logging to observers to trace events:
// In StockObserver
public function saving(Product $product)
{
\Log::debug('Product saving:', ['id' => $product->id, 'stock' => $product->stock]);
// ...
}
Validation Errors:
Check the validation_errors log channel for detailed validation failures:
// In your model
protected function validateArray(array $data)
{
$validator = Validator::make($data, $this->rules());
if ($validator->fails()) {
\Log::channel('validation_errors')->error($validator->errors());
throw new \Exception('Validation failed');
}
return true;
}
Database Queries:
Use Laravel Debugbar or DB::enableQueryLog() to inspect queries:
DB::enableQueryLog();
$product = Product::all();
\Log::info(DB::getQueryLog());
Custom Fields: Add custom attributes to the model and serialize them:
// In Product model
protected $casts = [
'custom_attributes' => 'array',
];
// In migrations
Schema::table('products', function (Blueprint $table) {
$table->json('custom_attributes')->nullable();
});
Additional Scopes: Extend the query builder with custom scopes:
public function scopeFeatured($query)
{
return $query->where('is_featured', true);
}
Event Customization: Publish and override event classes:
php artisan vendor:publish --tag="common-product-events"
Then extend the published events in app/Events.
API Extensions:
Add custom endpoints by extending the ProductController:
namespace App\Http\Controllers;
use Antimattr\CommonProduct\Http\Controllers\ProductController as CommonProductController;
class ProductController extends CommonProductController
{
public function customEndpoint()
{
// Your logic
}
}
Testing Utilities:
Extend the ProductFactory for your test needs:
namespace Database\Factories;
use Antimattr\CommonProduct\Database\Factories\ProductFactory as CommonProductFactory;
class ProductFactory extends CommonProductFactory
{
public function withCustomAttributes()
{
return $this->afterCreating(function (Product $product) {
$product->custom_attributes = ['key' => 'value'];
$product->save();
});
}
}
How can I help you explore Laravel packages today?