Using one entity in multiple controllers - symfony

The situations' setup is like this:
1) I have two bundles in which I would like to use the same entity. So as suggested in this question and then in this article, I created the CommonEntitiesBundle where the desired Entity lives.
2) Then I described the model interface in both bundles I want to use the Entity in. So far so good.
Now I would like to use the interfaced Entity, just like I would use a 'bundle-native' one like this:
$clientData = new Client(); // the Entity class
$client->setName('Greg');
How should I approach it when there is only the interface available?
I used the interface in this controller.
In the Symfony's example there is only a use of the class via the ORM annotations.

An interface is not a class. it is instead a list of minimal necessary methods a class with 'implements [interfaceName]' must have.
Instead you could provide a example class with your bundle and use that one by default. Add configuration in app/config.yml where you can change the classname. each derived class must implement the interface for consistence.

Related

How to inject an interface that is part of the framework using custom class in SilverStripe

I am working on a SilverStripe project. Project SilverStripe version is 4.4.4. In my project, I am trying to replace a class that injected for an interface. Both the interface and the class are part of the framework.
In the framework code, it has the following class
class NaturalFileIDHelper implements FileIDHelper
As you can see, the NaturalFileIDHelper is implementing the FileIDHelper interface. What I want to do is that I want to replace all the NaturalFileIDHelper instances with my customer class called CustomFileIDHelper class that is also implementing the FileIDHelper interface.
This is what I did.
I created a custom class called, CustomFileIDHelper that is implementing the FileIDHelper interface.
Then I added the following code snippet within the mysite.yml.
SilverStripe\Assets\FilenameParsing\FileIDHelper:
class: CustomFileIDHelper
Then I rebuild the project. But my project is still using the NaturalFileIDHelper class that comes with the framework. It seems like the CustomFileIDHelper class is not used.
How can I get it working? Is it possible to do that?
I want to replace all the NaturalFileIDHelper instances with my customer class called CustomFileIDHelper class
If you want to replace all NaturalFileIDHelper instances, then that's the class you should override (not the FileIDHelper). Also the .yml config you have needs to be passed to the Injector. (https://docs.silverstripe.org/en/4/developer_guides/extending/injector/)
SilverStripe\Core\Injector\Injector:
SilverStripe\Assets\FilenameParsing\NaturalFileIDHelper:
class: CustomFileIDHelper
But this will only work if the object is instantiated through the ClassName::create() function, and I found this piece of code in the framework:
(LegacyThumbnailMigrationHelper.php)
$defaultFileIDHelper = new NaturalFileIDHelper(), // Injector is not called here.
You will need to override the classes which call new NaturalFileIDHelper() and modify it to NaturalFileIDHelper::create(), you're able to call ::create() because the NaturalFileIDHelper class have the use Injectable; trait.
Also instead of going through the troubles of overriding classes which call new NaturalFileIDHelper(), you can create a pull request to the framework repository with your NaturalFileIDHelper::create() changes instead, as it would be an improvement to the current framework code (it enables the use of dependency injection).

Symfony 2 Override Entity and add extra properties

I have entity A and entity B which extend entity A.
Entity A - in vendor bundle.
Entity B - in /src bundle.
I search a lot of time and only solution works for me. It's using ClassMetadataListener on Doctrine Event loadClassMetadata.
But extending on entity B not works. I need add all properties form entity A to entity B.
I can't understand why.
You cannot simply extend a class like normally when using Doctrine2.
Check the documentation on chapter 6. Inheritance Mapping on how to implement inheritance on your entity classes.
You are probably interested in chapter 6.1. Mapped Superclasses.
A mapped superclass is an abstract or concrete class that provides persistent entity state and mapping information for its subclasses, but which is not itself an entity. Typically, the purpose of such a mapped superclass is to define state and mapping information that is common to multiple entity classes.
For this to work you have to use the #MappedSuperclass annotation to your entity base class.
Add #MappedSuperclass annotation in top of A. That would be enough :)

override mapping for a specific Entity class

I'm using a standard Symfony 2.8 framework with Doctrine.
My entities' mappings are all annotated, but I would need to map a single entity by using the PHP way (by defining loadMetadata static method) . I know there's a way to override a mapping Bundle configuration like explained here, but what I would like is specifying a single Entity. Is that possible? Thanks
No you can't mix the formats
A bundle can accept only one metadata definition format. For example, it's not possible to mix YAML metadata definitions with annotated PHP entity class definitions.
see doc here:
http://symfony.com/doc/current/doctrine.html
if you really need to change the mapping format then I suggest you create a new bundle for your specific entity. I also had this problem. I wished to have different mapping format (yml and annotation in my case) but I had to create a new bundle.

Symfony2 using a "global function" inside a form without overkill

I know I can do a lot of stuff...but I'd like to avoid overkill.
I have a class with general util methods. I want to be able to call one of those methods from inside a formType.
What I did is the following:
Inside the formType I added
use AppBundle\Util\GeneralUtil;
class HRMgmtFormType extends AbstractType
{
public function __construct(GeneralUtil $util)
{
$this->util = $util;
}
public function buildForm(FormBuilderInterface $builder, array $options)
{
$timeCommitment = $this->util->generatePercentVectorByStep(1);
...all the rest of the stuff
And when I call it from the controller I have:
$form = $this->createForm(new HRMgmtFormType(new GeneralUtil()), array(...all the stuff I need here...);
It works.
Is this ok/elegant?
I basically avoided declaring my general util class as a service, and most important declaring my form as a service (and injecting the generalUtil service inside).
Thank you!
SN
As far as you have the class "GeneralUtil" such simple as you have it now: without any dependencies and any configuration parameters, your solution is fair. But there are some more aspects that you should think about:
Will be "GeneralUtil" reusable at other places?
Will you need to make unit tests? if so, do you plan to mock the util if you plan to get "pure" unit test and make dependency injection(DI) with mock?
Do you plan extend the util's functionality? will it get some dependencies or configuration in future?
If yes, then it is better to get the benefit from Symfony SOA (Service Oriented Architecture) approach and refactor your code to SOA and DI, that will allow you follow another important patter as DRY (Don't repeat yourself) http://en.wikipedia.org/wiki/Don%27t_repeat_yourself
Update to your commet:
So you can see from http://symfony.com/doc/current/book/forms.html#defining-your-forms-as-services
Defining your form type as a service is a good practice and makes it
really easy to use in your application.
but as I already explained about the utils class, similar logic is about forms. So if you plan to reuse your form, then you can do it as service, but if you will have several different forms that will use your utils class, then it's better leave the form as class, but make a service for the utils. In addition you can do it if you see possibility to overwrite your form some other i.e 3rd party forms.
So benefits list from form as service:
in case of multiply usage, it initialized only once
easy to overwrite by other form
globally configured from parameters ans DI other services

How do I use more than one repository class in Symfony2?

I'm building an application in Symfony 2.2.
I use a custom repository class for one of my entities. I include it like this:
#ORM\Entity(repositoryClass="MyApp\MainBundle\Entity\CategoryRepository")
I want to use methods on my category entites from two other repository classes.
#ORM\Entity(repositoryClass="Gedmo\Sortable\Entity\Repository\SortableRepository")
#ORM\Entity(repositoryClass="Gedmo\Tree\Entity\Repository\NestedTreeRepository")
The entity, however, only expects one repositoryClass. How can I use all three of these repositories for the same entity?
I believe, you can only have one repository per Entity.
So you have referenced
ORM\Entity(repositoryClass="MyApp\MainBundle\Entity\CategoryRepository")
If you must use another repositories within the repository,
You can get the EntityManager for those Repositories (if not same) eg: $em2 = whatEverIsTheEntity()
Now you can have proxy methods in CategoryRepository to the methods in the repository you want to use. eg: $em2->getMethodFromEm2().
Note: But I wouldn't prefer above solution unless there is really a need. I would rather create a service layer which has Doctrine Entity Manager as Dependency Injected. This service layer will connect to all the repositories and collect final result whatever you want to achieve (to be used in the controller). This way, the code will be much cleaner and testable as well.
Hope this helps.
Repositories are class (so entity) reserved. If you inherit from super classes (even if those classes doesn't inherit directly, but repos can), than you can make your repository (CategoryRepository) extended by other two (one extend the other, since PHP doesn't allow more than one class extension).
Pretty much something like that
class NestedTreeRepository extends SortableRepository
{
}
class CategoryRepository extends NestedTreeRepository
{
}
don't know if this fits your needs, but this a way (and I'll not surprised if is the only one) for realize what you're trying to do here

Resources