If you have worked with Laravel long enough, you probably saw the Repository pattern used quite a lot 5-10 years ago. But it's not so popular now. Let me demonstrate.
This lesson will be a bit more opinionated but based on the strong opinions of a few more influential Laravel community members.
Typical Not-So-Practical Repository Example
In some Laravel tutorials, especially older ones, you may find example similar to this:
namespace App\Http\Controllers; use App\Repositories\UserRepository;use Illuminate\View\View; class UserController extends Controller{ public function __construct( protected UserRepository $users, ) {} public function show(string $id): View { $user = $this->users->find($id); return view('user.profile', ['user' => $user]); }}
Then, the Repository class may look something like this:
app/Repositories/UserRepository.php:
class UserRepository{ public function find($id) { return User::find($id); }}
But this doesn't seem very useful. Why a Repository with $this->users->find() instead of just User::find()? A separate class for doing the same thing?
Typically, the authors of those tutorials emphasize mocking or swapping the repository class with some other class in the future, for better testability and isolation.
But this typical over-simplified example above doesn't do the justice to the idea of Repository pattern in general.
Let me show you better examples, and then I will still debate that I do NOT approve of them in Laravel.
More Practical Repository Example
To actually show the "swapping" and "mocking" idea from the same example above, it should be...
This lesson appears to assume a 1-to-1 relationship between Repositories and Models, however I am really not sure that this is a requirement nor indeed necessarily a good thing.
As I understand it the purpose of a repository is to provide a data access layer between the business logic and the database (models), so (taking a as an example Orders, and Order lines which would be separate models) you might have a single Order repository to handle as an integrated whole the access to existing, and creation of new, orders with their associated order lines, ensuring for example, that creation of an Order has insertion of an order record and associated order lines as part of an atomic transaction, and that the created Order is a valid one (e.g. has at least 1 order line etc.).
A bit more controvercially (because it seems to mix database access with business logic), as part of the transaction it might also e.g. check that the on-hand/uncommitted quantity for each order line item is sufficient to cover the order.
It also seems to me that to some extent it might also align with Domain Driven Design principles.
Repositories are often used as 1-to-1 with Models, but that indeed doesn't mean that it's a must.
Of course, a 1-to-1 map means that you have full control of actions and full customization of your workflow.
But at the same time, you are correct about the fact that Repository is "Data access layer". And the data can be anything. From array to API calls or database. It really doesn't matter, as long as the repository returns unified data and methods.
It really depends on the implementation in the application. I've seen mix of all the things you said and we have, but... Couldn't 100% say that - this is it, we have to use it like X and never go to the side :)