I'm using Doctrine Filters in my Symfony 3.x application.
Now, I need to filter a joined table, but the addFilterConstraint method allways receive the inheritance root:
In the case of joined or single table inheritance, you always get passed the ClassMetadata of the inheritance root. This is necessary to avoid edge cases that would break the SQL when applying the filters.
This is (part of) my class:
<?php
namespace AppBundle\Filter;
use Doctrine\ORM\Mapping\ClassMetaData;
use Doctrine\ORM\Query\Filter\SQLFilter;
class DataAbstractionFilter extends SQLFilter
{
public function addFilterConstraint(ClassMetadata $targetEntity, $targetTableAlias)
For example, if I had the classes:
class SomeClass {/**/}
class SomeChildClass1 extends SomeClass {/**/}
class SomeChildClass2 extends SomeClass {/**/}
addFilterConstraints will allways receive SomeClass.
Any suggestions? Thanks in advance.
Related
I encountered this issue using the repository pattern. Currently I use an interface, and a custom class to achieve it, then type-hint it into the controller's construct and because of Laravel, it will solve the repositories' dependencies automatically and recursively.
I also do this in a service provider:
$this->app->bind(path/to/repoInterface,path/to/implementationClass)
However, because of the way I coded these repositories, in order to avoid code duplication, I created an abstract class that has a common method to all these repositories. This class is as follows:
abstract class CommonRepo{
public function __construct(SomeModelClass model){}
public function commonMethod(){//Code here}
And my repositories have the following structure:
public class ExampleRepository extends CommonRepo implements ExampleRepositoryI{
public function __construct(){
parent::__construct();
}
}
Laravel doesn't like this, so its giving this error:
Argument 1 passed to path/to/repo/CommonRepo::__construct() must be an instance of path/to/model/SomeModelClass, none given, called in...
So, obviously is not resolving the dependency of the class CommonRepo, but it does resolve the dependencies on the normal repositories.
I'd like, if it's possible, to use type-hinting (the Laravel way) without having to do anything related to the new operator
How can I, then, resolve that class's dependencies ?
PD: Using Laravel 5.2
Parent constructor is called like normal function without touching dependency resolver so you should do one of two possibilities:
public class ExampleRepository extends CommonRepo implements ExampleRepositoryI
{
public function __construct(SomeModelClass $model){
parent::__construct($model);
}
}
or
public class ExampleRepository extends CommonRepo implements ExampleRepositoryI
{
public function __construct(){
parent::__construct(App::make(SomeModelClass::class));
}
}
nice question. I did some tinkering, though I don't know if this is what you're looking for. But you can dynamically create an instance of Eloquent model required by your repository class.
Let's say you have your User model class stored in app\Models\User.php:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class User extends Model
{
//
}
You then create a base abstract class for all of your repository classes: app\Repositories\BaseRepository.php. This is where you place all common functionalities for your repository classes. But rather than injecting the Eloquent instance through the constructor, you may add a method named getModel() to dynamically create an instance of Eloquent model for your repository.
<?php
namespace App\Repositories;
use ReflectionClass;
use RuntimeException;
use Illuminate\Support\Str;
abstract class BaseRepository
{
protected $modelNamespace = 'App\\Models\\';
public function getById($id)
{
return $this->getModel()->find($id);
}
public function getModel()
{
$repositoryClassName = (new ReflectionClass($this))->getShortName();
$modelRepositoryClassName = $this->modelNamespace . Str::replaceLast('Repository', '', $repositoryClassName);
if (! class_exists($modelRepositoryClassName)) {
throw new RuntimeException("Class {$modelRepositoryClassName} does not exists.");
}
return new $modelRepositoryClassName;
}
}
Now let's say you want to create a repository for your User model, and this user's repository must implement the following interface: app\Repositories\UserRepositoryInterface.php
<?php
namespace App\Repositories;
interface UserRepositoryInterface
{
public function getByEmail($email);
}
You create app\Repositories\UserRepository.php class and simply extend it from the BaseRepository class. Also don't forget to implement all specific implementations defined on UserRepositoryInterface.
<?php
namespace App\Repositories;
use App\Repositories\BaseRepository;
use App\Repositories\UserRepositoryInterface;
class UserRepository extends BaseRepository implements UserRepositoryInterface
{
public function getByEmail($email)
{
return $this->getModel()->where('email', $email)->firstOrFail();
}
}
This way you can bind the UserRepositoryInterface to it's implementation like so:
$this->app->bind(\App\Repositories\UserRepositoryInterface::class, \App\Repositories\UserRepository::class);
Finally you can freely inject the UserRepositoryInterface to a controller's constructor or methods. You can also resolve it via service container like this:
$userRepository = App::make(App\Repositories\UserRepositoryInterface::class);
$userRepository->getByEmail('john#example.com');
Of course there's a catch to this approach. The repository class should be started with the associated model, so the InvoiceRepository.php is dedicated for Invoice.php model class.
Hope this help!
This might help. You can listen in for when an object resolves and set attributes.
$this->app->resolving(CommonRepo::class, function ($object, $app) {
// Called when container resolves object of any type...
$object->commonObject = app(CommonObject::class);
});
Docs: https://laravel.com/docs/5.4/container#container-events
I'm facing the following issue in PHPStorm 9:
Say I have an interface FieldInterface that has some methods:
namespace Acme;
interface FieldInterface {
public function methodA();
public function methodB();
}
then I have an abstract class that implements base functionality of the interface. That abstract class has the user to implement certain methods, let's say it's methodB in our example:
namespace Acme;
abstract class AbstractField implements FieldInterface {
public function methodA() {
// implement methodA
}
public abstract function methodB(); // have the user implement it
}
And finally I have some ready-to-use class StringField:
namespace Acme;
class StringField extends AbstractField {
public function methodB() {
// implement methodB
}
}
At this point everything's going well. But if I add new method in the FieldInterface, PHPStorm does not say that anything is wrong with AbstractField while it's obvious that I should add public abstract function newMethod(); in there. However, it spots the error in StringField class instead.
It could be understood from the point that abstract classes are made for the purpose of extention, but usually you extend the abstract class rather than implement underlying interface. The whole meaning of making abstract class is to save user's time for implementing the interface. So why PHPStorm forces me to implement interface in concrete class rather than forcing me to implement it in abstract class that is explicitly implements the interface.
So I wonder if it is a bug in PHPStorm, or maybe it's done on purpose. Either way, is there any workaround?
That's how it should be, showing an error in the abstract class would be wrong.
In fact, public abstract function methodB(); is redundant because the abstract class already "inherits" this abstract method from the interface as it does not implement it.
The only workaround is to make AbstractField not abstract.
This might be a silly question. I am trying to create a generic repository interface for Doctrine 2 so I can pass it into my controller through direct injection:
//TestController.php
public function __construct(TestRepositoryInterface $p_repository){
//...
}
The method signature for an EntityRepository in Doctrine2 is as follows:
class EntityRepository implements ObjectRepository, Selectable{
//...
}
EntityRepository is missing a few functions that I would like to have in a repository(Add, Delete, Update). So I created a base repository interface and an abstract repository class to encapsulate those functions:
interface RepositoryInterface {
public function add($entity);
public function delete($entity);
public function update($entity);
}
The abstract repository class extends from EntityRepository so I can still get the functionalities of EntityRepository.
abstract class AbstractRepository extends EntityRepository{
public function add($entity){
//...
}
public function add($entity){
//...
}
public function add($entity){
//...
}
}
To tie everything together, I made TestRepositoryInterface extend from RepositoryInterface, ObjectRepository, and Selectable.
interface TestRepositoryInterface extends RepositoryInterface, ObjectRepository, Selectable{
}
Then I can just pass in an implementation of TestRepositoryInterface through direct injection:
class TestImplementation extends AbstractRepository implements TestRepositoryInterface{
//...
}
Or if I am unit testing, it would be easy to create a mock object or test stub.
My only concern is in TestImplementation class. It extends AbstractRepository which already implements ObjectRepository and Selectable (through EntityRepository), and at the same time TestImplementation also implements TestRepositoryInterface which also extends ObjectRepository and Selectable. So TestImplementation is essentially implementing ObjectRepository and Selectable twice (or is it?). It compiles just fine, but is this a valid approach?
It is perfectly OK for a class to implement multiple interfaces that in turn extend common interfaces. The methods are the same so there are no conflicts.
The only thing you need to worry about is implementing interfaces with same named methods but alternative arguments.
Say you have an interface whose implementations need to be iterated. You would probably have it implement\IteratorAggregate.
Now lets say your implementation class extends ArrayCollection (from doctrine common). Because ArrayCollection also implements IteratorAggregate it take care of some of your own interface's definitions for you.
When it comes to mixing interfaces, look for issues with compatibility rather than inheritance.
I have a few standard classes:
abstract class Parent {}
class Child1 extends Parent {}
class Child2 extends Parent {}
The Parent class contains logic common to both child classes, but each child class has its own additional logic.
For each of my clients, this logic can be configurable. So for any particular client, I may have:
abstract class ClientParent {}
class ClientChild1 extends ClientParent {}
class ClientChild2 extends ClientParent {}
The problem I'm having is how to get the logic from the standard classes into these ones. The first approach would be something like this:
abstract class ClientParent extends Parent {}
Okay, now I have the standard parent logic in all my client-specific classes. Great. But the child classes are already extending ClientParent, so we can't do the same thing for them. My "solution" is then to do this:
abstract class Parent {}
class Child1 extends ClientParent {}
class Child2 extends ClientParent {}
abstract class ClientParent extends Parent {}
class ClientChild1 extends Child1 {}
class ClientChild2 extends Child2 {}
There; now all the appropriate logic is passed down and everybody's happy. Except that now my standard classes are coupled to a particular client. As I have many clients, this is obviously no good.
What's my out here? Is there a way to address this through inheritance alone, or should I look into more complex configuration injection strategies?
Edit:
I'm using PHP 5.3, so I am unable to use traits to solve this problem.
PHP does not support multiple inheritance which is what I think you are trying to approximate here. It does however support (as of 5.4) traits, which in many cases can provide you with comparable functionality.
trait ParentTrait {
public function someUsefulMethod(){/*...*/};
public function someOtherUsefulMethod(){/*...*/};
}
abstract class ClientParent(){}
class ClientChild1 extends ClientParent {
use ParentTrait;
}
$clientChild1 = new ClientChild1();
$clientChild1->someUsefulMethod();
Another option would be to use composition instead, possibly for your problem employing the Strategy pattern would work.
class SuperWidget extends Widget{
private $dataStrategy;
public function __construct(DataStrategy $strategy){
$this->dataStrategy = $strategy;
}
// do this if you need to expose the functionality.
public function getData(){
return $this->dataStrategy->getData();
}
// or if you are just using it in your class
public function renderWidget($option){
$data = $this->dataStrategy->getData($option);
// use the data to render the widget;
return $renderedWidget;
}
}
$dataStrategy = JsonDataStrategy("http://data.source.url/jsonService.php");
$widget = new SuperWidget($dataStrategy);
I got this problem I have a method that is repetitive in all the Repositories, for example this method.
function getAllOrderedBy($column) {
$qb = $this->createQueryBuilder('ac')
->select('ac')
->orderBy('ac.' . $column);
return $qb->getQuery()->getResult();
}
I want to extract it in another superclass, OrderedRepository for example and use it as the base class for all the other repositories.
Now the problem is how to do that ?
I tried to instantiate EntityRepository in the constructor of the OrderedRepository,
something like this, but also instantiating there all the internal objects, needed for other stuff, but it didn't really worked, and I felt it is the wrong path to follow.
function __construct() {
parent::__construct();
$this->blabla_option = "instantiated";
}
Could you please give an example of correct extending of EntityRepository so than this extended class could serve as a base class for other repositories ?
P.S. I'm a begginer in PHP so please excuse me if I hurt your feelings with my unawareness.
This is more a Doctrine2 thing.
Assuming you are using annotations for your doctrine mapping, you have to declare which repository class you are using in the Entity:
/**
* #ORM\Entity(repositoryClass="Fully\Qualified\Namespace\To\MyRepository")
*/
class MyEntity { }
as explained here: http://symfony.com/doc/2.0/book/doctrine.html#custom-repository-classes .
Then, you can code this custom MyRepository class, using standard class inheritance.
You could imagine something like that:
class OrderedRepository extends EntityRepository
{
// some extra methods...
}
class MyRepository extends OrderedRespository {}
Finally, if you want to override the __constructor of your repository, you have to initailize the parent constructor with the same arguments:
public function __construct($em, Mapping\ClassMetadata $class)
{
parent::__construct($em, $class);
// some extra stuff
}