is it possible to have two repositories for the same entity ?
I try something like that, but it does not work..
class PackageRepository extends EntityRepository
{
public function __construct($em, Mapping\ClassMetadata $class)
{
$cmf = $em->getMetadataFactory();
$class = $cmf->getMetadataFor('Product');
parent::__construct($em, $class);
}
}
Any ideas ?
First of all, why would you want to do that?
Second, to answer your question. You can have as many repositories working with same entities as you like, they are just simple classes after all.
But you can link only one class with the entity class using the #Repository annotation (or YAML, or XML, whatever). All the mapping data is stored in the EntityManager. EntityManager will know only one repository class is linked with the entity class, if you try to get it with $entity->getReposiotry() or similar it will return only the linked class.
But nothing can stop you from creating your own classes which do some queries and call them directly, explicitly, without relying on the EntityManagers repository mapping.
I also have run into the same issue, when two Symfony bundles work with same entity, but queries are different for each bundle, so I also decided create separate repository for each bundle. For Doctrine 2 the solution can be:
"#Repository annotation" can be used to point default repository or just avoid it.
Create usual repository class, e.g. "MyEntityRepository".
In code:
$myEntityRepo = new MyEntityRepository($entityManager, $entityManager->getClassMetadata('AppBundle:MyEntity'));
I found the solution. Something in PHP called Trait.
Example:
class UserRepository extends EntityRepository {
public function adminQuery1();
public function adminQuery2();
public function adminQuery3();
public function adminQuery4();
public function adminQuery5();
public function adminQuery6();
public function frontEndQuery1();
public function frontEndQuery2();
public function frontEndQuery3();
public function frontEndQuery4();
public function frontEndQuery5();
public function frontEndQuery6();
}
Now keep on going with different specific query portions and your repository quickly becomes confusing and too long.
You could
Create multiple repository that inherit each other OR
Use traits
Example Below:
Trait AdminQuery {
public function adminQuery1();
public function adminQuery2();
public function adminQuery3();
public function adminQuery4();
public function adminQuery5();
public function adminQuery6();
}
Trait FrontEndQuery {
public function adminQuery1();
public function adminQuery2();
public function adminQuery3();
public function adminQuery4();
public function adminQuery5();
public function adminQuery6();
}
The actual repository class.
class UserRepository extends EntityRepository {
use AdminQuery;
use FrontEndQuery;
}
The beauty of this is that the keyword this will refer to the context in which the the Trait was used meaning you have access to all functions of EntityRepository
Finally found a use case for Trait.
Related
Peeps, I'm lost. Tried everything and after 5 hours of searching through the 10th page of Google hits, I give up. Maybe I just dont know how to ask Google the correct keywords..
I have this scenario: In lumen app, lets call it X, I have require custom packages CRUD and Storage, Storage is using functionality of CRUD.
StorageService has:
use Crud\Services\BaseService;
class StorageService extends BaseService{}
And Crud\BaseService has constructor, that uses Model:
use Illuminate\Database\Eloquent\Model;
class BaseService
{
protected $model;
public function __construct(Model $model)
{
$this->model = $model;
}
}
When I try to do anything with my app X, I get error:
Target [Illuminate\Database\Eloquent\Model] is not instantiable while building [Lumee\Storage\Services\StorageService]
I cannot get my head around how to get to proper class of Model, since I saw, that Model is abstract class.
Also, I'm using this CRUD package successfully in another App, only difference is, there CRUD is used directly in app, not via some other package. I'm confused, why there is working without any additional bindings and service registering..
EDIT: Added some binding into StorageServiceProvider (boot and register methods):
$this->app->bind(BaseService::class, function(){
return new BaseService(new Model());
});
And registered StorageServiceProvider in my boostrap/app.php:
$app->register(Storage\Providers\StorageServiceProvider::class);
Thing still returns same error. I tried with binding in CrudServiceProvider, nope.
you can't get object from abstract class (Model class) to solve this try this :
use Illuminate\Database\Eloquent\Model;
class BaseService
{
protected $model;
}
suppose your model is (Storage) :
use Crud\Services\BaseService;
class StorageService extends BaseService{
public function __construct(Storage $model)
{
$this->model = $model;
}
}
There is a model data:
class Order extends Model
{
}
How to write a custom method inside the Order class so that it can be called in constructor like this:
Order::myMethod()
Order->myMethod()
Where myMethod is:
public function myMethod() {
return DB::query(<SQL QUERY>);
}
Purpose is to move SQL queries inside model's class, that don't mess this code in controllers.
Rather create a custom function in Model, You can use traits to achieve the desired output.
Please follow either steps:-
https://medium.com/#kshitij206/traits-in-laravel-5db8beffbcc3
https://www.conetix.com.au/blog/simple-guide-using-traits-laravel-5
Guess you are asking about the static functions:
class Order extends Model {
public static function myMethod() {
}
}
and you can call it anywhere like
Order::myMethod();
You can achieve the desired behavior using magic methods __call and __callStatic
if your real method is static you can use __call() to intercept all "non static" calls and use it to call the static and use __callStatic to forward the calls to a new instance to that class .
Your methods should be always static because if a non static method exists and you are calling it statically php raises an error
Non-static method Foo::myMethod() should not be called statically
No problem if your method is static
class Order extends Model {
public static function myMethod() {
return static::query()->where(...)->get(); // example
}
public function __call($name, $arguments) {
return forward_static_call_array([__CLASS__, $name], $arguments);
}
public static function __callStatic($name, $arguments) {
return call_user_func_array([app(__CLASS__), $name], $arguments);
}
}
(new Order())->myMethod();
Order::myMethod();
I can't understand your exact problem is. but if you are using laravel, then you can write custom method inside the ABC model like this
class ABC extends Model
{
//here is your fillable array;
public function abc()
{
//Here is your Eloquent statement or SQL query;
}
}
just call this abc() method inside the controller like this
use ABC;
class AbcController extends Controller
{
private $_abc; // it is private variable
// this is constructor
public function __construct(ABC $abc)
{
$this->_abc= $abc;
}
public function abcMethod()
{
$this->_abc->abc();
}
}
Thanks
I don't believe I'm understanding your intention. You've stated:
Purpose is to move SQL queries inside model's class, that don't mess this code in controllers.
Why does the Order->myMethod() need calling inside the constructor? If you're trying to design your data access layer to work efficiently, you can use data repositories.
To maintain my Laravel application and save myself from a lot of duplicate code I have made the following solution:
BaseController
class BaseController extends Controller
{
public function get($id){
return $this->baseService->get($id);
}
public function getAll(){
return $this->baseService->getAll();
}
}
BaseService
class BaseService
{
protected $model;
public function __construct($model){
$this->model = $model;
}
public function get($id){
return response()->json($this->model->where('id', $id)->first());
}
public function getAll()
{
return $this->model->get();
}
}
MyController
class MyController extends BaseController
{
protected $model;
protected $baseService;
public function __construct(){
$this->model= new Model();
$this->baseService = new BaseService($this->model);
}
/**
* This controller has all the functionality from BaseController now
*/
}
What I'm wondering if this is a good method. Should I stick with this or should I use a different approach? I've heard about Traits but not sure if they are doing the same thing. It's Laravel 5.5 I'm using.
Yes, traits are used to move methods out of a controller regularly. A good example that the Laravel framework uses is the ThrottlesLogin trait. Take a look at https://github.com/laravel/framework/blob/5.5/src/Illuminate/Foundation/Auth/ThrottlesLogins.php#L20
to see how the methods are moved outside of a controller but can be still accessed by importing the trait using the use keyword.
While traits would work for your use case I wouldn't use them here for the functionality you are looking for. I would use the repository pattern. It would better separate your code and make it more reusable.
Take a look at https://bosnadev.com/2015/03/07/using-repository-pattern-in-laravel-5/ for more information on the repository pattern. Basically, you would separate your code into a separate repository and use Laravel's built in IoC to inject the repository into your controller.
MyController
class MyController extends Controller
{
protected $repo;
public function __construct(MyRepository $myRepository)
{
$this->repo = $myRepository;
}
public function index()
{
$myStuff = $this->repo->all();
}
// you can also inject the repository directly in the controller
// actions.
// look at https://laravel.com/docs/5.5/controllers#dependency-injection-and-controllers
public function other(MyRepository $repo)
{
$myStuff = $repo->all();
}
}
This is the perfect use case for a Trait. Traits are intended for reusable functions. They're super simple to implement, and won't take more than a few minutes to change what you have.
Here is a great article on them: https://www.conetix.com.au/blog/simple-guide-using-traits-laravel-5
We are trying to develop an Android app that required a REST API to show data from web server.
We tried to use Laravel resource to create REST service like below:
Route::resource('list', 'ListController');
namespace App\Http\Controllers;
use mymodel
class ListController extends Controller
{
public function getShow($id)
{
$Jsondata=list($id);
return $JsonData;
}
}
But it's not working as expected need some token key or some other authentication and authorization need to know how to set.
We should utilize the Repository / Gateway design pattern:
For example, when dealing with the User model, first create a User Repository. The only responsibility of the user repository is to communicate with the database (performing CRUD operations). This User Repository extends a common base repository and implements an interface containing all methods we require:
class EloquentUserRepository extends BaseRepository implements UserRepository
{
public function __construct(User $user) {
$this->user = $user;
}
public function all() {
return $this->user->all();
}
public function get($id){}
public function create(array $data){}
public function update(array $data){}
public function delete($id){}
// Any other methods we need go here (getRecent, deleteWhere, etc) }
Then, create a service provider, which binds your user repository interface to your eloquent user repository. Whenever you require the user repository (by resolving it through the IoC container or injecting the dependency in the constructor), Laravel automatically gives you an instance of the Eloquent user repository you just created. This is so that, if you change ORMs to something other than eloquent, you can simply change this service provider and no other changes to your codebase are required:
use Illuminate\Support\ServiceProvider;
class RepositoryServiceProvider extends ServiceProvider {
public function register() {
$this->app->bind(
'lib\Repositories\UserRepository', // Assuming you used these
'lib\Repositories\EloquentUserRepository' // namespaces
);
}}
Next, create a User Gateway, who's purpose is to talk to any number of repositories and perform any business logic of your application:
use lib\Repositories\UserRepository;
class UserGateway {
protected $userRepository;
public function __construct(UserRepository $userRepository) {
$this->userRepository = $userRepository;
}
public function createUser(array $input)
{
// perform any sort of validation first
return $this->userRepository->create($input);
}}
Finally, create our User web controller. This controller talks to our User Gateway:
class UserController extends BaseController
{
public function __construct(UserGatway $userGateway)
{
$this->userGateway = $userGateway;
}
public function create()
{
$user = $this->userGateway->createUser(Input::all());
}}
By structuring the design of your application in this way, you get several benefits: you achieve a very clear separation of concerns, since your application will be adhering to the Single Responsibility Principle (by separating your business logic from your database logic) . This enables you to perform unit and integration testing in a much easier manner, makes your controllers as slim as possible, as well as allowing you to easily swap out Eloquent for any other database if you desire in the future.
For example, if changing from Eloquent to Mongo, the only things you need to change are the service provider binding as well as creating a MongoUserRepository which implements the UserRepository interface. This is because the repository is the only thing talking to your database - it has no knowledge of anything else. Therefore, the new MongoUserRepository might look something like:
class MongoUserRepository extends BaseRepository implements UserRepository
{
public function __construct(MongoUser $user) {
$this->user = $user;
}
public function all() {
// Retrieve all users from the mongo db
}}
And the service provider will now bind the UserRepository interface to the new MongoUserRepository:
$this->app->bind(
'lib\Repositories\UserRepository',
'lib\Repositories\MongoUserRepository'
);
Throughout all your gateways you have been referencing the UserRepository, so by making this change you're essentially telling Laravel to use the new MongoUserRepository instead of the older Eloquent one. No other changes are required.
Number one is that what is not working? if you mean that the response is not showing then its because your function actually doesn't even seem to be going any where, and number two are you new to Laravel?
Okay if this is what you have:
namespace App\Http\Controllers;
use mymodel;
class ListController extends Controller
{
public function getShow($id)
{
$Jsondata=list($id); return $JsonData;
}
}
Then I can be safe to say a lot of things is wrong.
To my understanding using Route::resource('list', 'ListController'); in your routes file creates show, edit, update and destroy paths, and also expects to see these functions too (I have not proven this to the core though) but thats my understanding.
so you can simply start laravel life by doing having your routes with
Route::get('list', 'ListController#show')
Then change your ListController to something like this:
namespace App\Http\Controllers;
use mymodel;
class ListController extends Controller
{
public function show($id)
{
$jsonData= ['my' => 'json data'];
return $jsonData;
}
}
If this works for you, then cool else so many thing might already be wrong with your setup.
Note: you might need to take your time to learn to use laravel from the documentation page. If API is your interest then try DIngo api. Also try to learn to use markup :)
Hope this helps :)
I am confused, again, about implementing OOP in CodeIgniter.
By design, I have two classes, namely Customer and Supplier. Both classes extends a super class I call Institution.
It was not a problem for me when I wrote them using only php (without framework).
class Customer extends Institution {
public function __construct() {
parent::__construct();
}
}
class Supplier extends Institution {
public function __construct() {
parent::__construct();
}
}
class Institution extends DBConnection {
public function __construct() {
parent::__construct();
}
}
class DBConnection {
// do CRUD activities here
}
The questions are:
How do I write them using CI?
Is it as Controller or in Model the best way to implement them? What factors should be considered?
A friend suggested a way I thought a bit hacky, model extends model. I try, if possible, to do it in codeigniter-appropriate way.
Any suggestion will be appreciated.
Well, as it is going to be your domain model entities, it should be models. And there is nothing wrong in having another model class as a base class for model class. So, you'll just need to make your Institution (or DBConnection if you prefer to keep it) class extend CI_Model.