bugover/laravel-repository
Laravel package providing a repository layer to abstract Eloquent data access. Includes base repository classes, common CRUD methods, query helpers, and patterns for cleaner, testable service code in your Laravel apps.
Install the Package:
composer require bugover/laravel-repository
Publish the configuration:
php artisan vendor:publish --provider="BUGOVER\Repository\RepositoryServiceProvider" --tag="config"
Configure the Package:
Update config/repository.php to define:
redis, file).60 seconds).Create Your First Repository:
Generate a repository class for a model (e.g., User):
php artisan make:repository UserRepository
(Note: The package doesn’t include a built-in make:repository command, so manually create a class extending BUGOVER\Repository\BaseRepository.)
Example:
namespace App\Repositories;
use App\Models\User;
use BUGOVER\Repository\BaseRepository;
class UserRepository extends BaseRepository
{
protected $model = User::class;
protected $cacheTags = ['users']; // Cache tags for invalidation
}
Bind the Repository in AppServiceProvider:
public function register()
{
$this->app->bind(
\App\Repositories\UserRepository::class,
function ($app) {
return new \App\Repositories\UserRepository();
}
);
}
First Usage: Inject the repository into a controller or service:
use App\Repositories\UserRepository;
class UserController extends Controller
{
protected $userRepository;
public function __construct(UserRepository $userRepository)
{
$this->userRepository = $userRepository;
}
public function show($id)
{
// Automatically cached with tag 'users'
return $this->userRepository->find($id);
}
}
Test Caching:
find() twice; the second call should return cached data.update() or delete(); verify cache invalidation using the users tag:
php artisan cache:tags
Replace direct Eloquent calls with repository methods:
// Before
$user = User::find(1);
$user->update(['name' => 'John']);
// After
$user = $this->userRepository->find(1);
$this->userRepository->update($user->id, ['name' => 'John']);
find(), all(), or custom queries by setting $cacheTags in the repository.cacheFor() to override TTL or tags:
$this->userRepository->cacheFor(300)->find($id); // Cache for 5 minutes
create(), update(), or delete() if tags are defined.Extend the repository to add custom scopes:
namespace App\Repositories;
use BUGOVER\Repository\BaseRepository;
class UserRepository extends BaseRepository
{
// ...
public function scopeActive($query)
{
return $query->where('active', true);
}
public function getActiveUsers()
{
return $this->scopeQuery(function ($query) {
return $this->scopeActive($query);
});
}
}
Usage:
$activeUsers = $this->userRepository->getActiveUsers();
Load relationships with caching:
// Automatically caches posts with tag 'posts'
$this->userRepository->with('posts')->find($id);
// Create multiple records (cached with 'users' tag)
$this->userRepository->createMany([
['name' => 'Alice'],
['name' => 'Bob'],
]);
// Delete multiple records (invalidates cache)
$this->userRepository->delete([1, 2, 3]);
Use repositories in a service layer to decouple business logic:
class UserService
{
protected $userRepository;
public function __construct(UserRepository $userRepository)
{
$this->userRepository = $userRepository;
}
public function registerUser(array $data)
{
$user = $this->userRepository->create($data);
// Trigger cache invalidation for related tags
Cache::forget(['users', 'user:' . $user->id]);
return $user;
}
}
Cache API responses using repository tags:
use Illuminate\Http\Resources\Json\JsonResource;
class UserResource extends JsonResource
{
public function toArray($request)
{
return [
'id' => $this->id,
'name' => $this->name,
'posts' => PostResource::collection($this->whenLoaded('posts')),
];
}
}
// In controller:
public function index()
{
return UserResource::collection(
$this->userRepository->all()
);
}
Listen for model events to invalidate cache:
// In a service provider
public function boot()
{
User::updated(function ($user) {
Cache::tags(['users'])->flush();
});
}
Mock repositories in tests:
$mockRepository = Mockery::mock(UserRepository::class);
$mockRepository->shouldReceive('find')
->once()
->with(1)
->andReturn(new User());
// Inject into controller/service
$controller = new UserController($mockRepository);
Override default cache keys:
protected function getCacheKey($id = null)
{
return 'custom_user_' . ($id ?? 'all');
}
update()/delete() calls may miss cache invalidation if not using transactions.DB::transaction(function () use ($user) {
$this->userRepository->update($user->id, ['name' => 'John']);
});
find() after cache invalidation can overwhelm the database.cacheFor() with longer TTLs for critical endpoints or implement a cache warming strategy.illuminate/http dependency).composer.json for conflicts and manually patch if needed:
"require": {
"laravel/framework": "^11.0",
"illuminate/http": "^11.0"
}
make:repository Commandphp artisan make:repository UserRepository --model=User
(Create a custom command or use a package like laravel-shift/repositories for scaffolding.)file) may not support tag invalidation reliably.redis or memcached for production:
'cache' => [
'driver' => env('CACHE_DRIVER', 'redis'),
],
creating/updating events, which may conflict with existing listeners.config/repository.php:
'listeners' => [
'creating' => false,
'updating' => false,
],
Then manually handle cache invalidation in your own listeners.php-cs-fixer to standardize your repositories:
composer require --dev friendsofphp/php-cs-fixer
vendor/bin/php-cs-fixer fix
php artisan cache:tags
php artisan cache:clear
\BUGOVER\Repository\BaseRepository::setDebug(true);
Enable Eloquent query logging to verify
How can I help you explore Laravel packages today?