I'm building a web application using Laravel. I use the repository pattern as my data layer.
Imagine there's some entity like Product
and a product can be assigned to a ProductCategory
.
The Product
has a field named category_id
and that field either contains the id
of a product category, or null
if the product doesn't belong to any category.
Both Product
s and ProductCategory
s can be created, updated, read, and deleted.
I use two repositories, one for Product
and one for ProductCategory
.
When a product category is deleted, the category_id
field of the products that belong to that category must be set to null
. (The products are decoupled from the deleted category and don't belong to any category anymore).
This means we are affecting ProductCategory
and Product
data.
Say for example I've got the following method in the ProductCategoryRepository
.
public function delete(int $categoryId)
{
ProductCategory::where('id', '=', $categoryId)->delete();
// Now all products that belong to the deleted category must have their category_id set to null.
Product::where('category_id', '=', $categoryId)->update(['category_id' => null]);
}
This means the repository...
- Actually contains kind of business logic.
- Affects data of multiple entities. (Actually don't know how bad this is).
The alternative I can think of, is just moving the Product
update (setting category_id
to null
) to the ProductRepository
.
public function changeProductsCategoryId(?int $oldValue, ?int $newValue)
{
Product::where('category_id', '=', $oldValue)->update(['category_id' => $newValue]);
}
Then, in a service where we handle the deletion of a category, we should call both repository methods.
// class ProductCategoryService
public function deleteCategory(int $categoryId)
{
$this->_categoryRepo->delete($categoryId);
$this->_productRepo->changeProductsCategoryId($categoryId, null);
}
One advantage of the second option is that when we have another business case like moving products to another category, we can use the changeProductsCategoryId
method of the ProductRepository
.
A third option would be nested repositories. Like the ProductCategoryRepository
get a ProductRepository
instance injected in its constructor and calls changeProductsCategoryId
on it in its delete
method. But I'm a bit hesitant about this, because this can lead to the problem of circular dependencies. For example when the ProductRepository
also ever needs a reference to the ProductCategoryRepository
.
I would like to know which option is best design and if there are possible problems with this approach? Or is there a better solution (based on experience and eventually literature).