I have an abstract controller which has a function getStatusAction to render a status based on the entity which will come as parameter.
abstract class HumanController extends CRUDController
{
public function getStatusAction(Human $human)
{
// return new Response ...
}
}
Human is an entity class but also abstract. One child is for example man.
So i create a ManController
class ManController extends HumanController
{
}
If i now call in my twig template the getStatusAction with something like
{{ path('path_to_man_controller_status_action', {'human' : '12'}) }}
An error occurs, because some mechanism tries to guess that my parameter is a human which is an abstract class. But this cannot be instantiated.
The provided class Human is abstract, and can not be instantiated
Instead i want that the mechanism should load the entity from my concrete class, in this case Man.
Yes, the framework can't know what class is meant, but how to solve this? I want to have one central function which is doing something with all my entities which are childs of the Human class. I don't want to write the getStatusAction function over and over again in my child classes.
So, is it at least possible to overwrite the abstract class function definition (with getStatusAction(Man $man)) but keep the implementation of the original class?
If not, is it possible to turn off the symfony automatic entity loading mechanism to pass just the entities trough my function?
Some other smart ideas? Thanks.
Related
I have the following user model:
use Illuminate\Contracts\Auth\MustVerifyEmail;
class User extends Model implements AuthenticatableContract,
AuthorizableContract,
CanResetPasswordContract,
MustVerifyEmail {
use Authenticatable, Authorizable, CanResetPassword, HasApiTokens, Notifiable;
public function sendEmailVerificationNotification()
{
$this->notify(new VerifyEmailNotification());
}
//...
}
However I'm getting the following error:
Class App\User contains 2 abstract methods and must therefore be declared abstract or implement the remaining methods (Illuminate\Contracts\Auth\MustVerifyEmail::hasVerifiedEmail, Illuminate\Contracts\Auth\MustVerifyEmail::markEmailAsVerified)
Why do I need to implement these two methods. Theres nothing in the docs regarding this?
Those methods are available in the MustVerifyEmail trait.
use \Illuminate\Auth\MustVerifyEmail;
See: Illuminate/Auth/MustVerifyEmail.php
Either add this trait and/or overload whatever you want, or add the other 2 methods and add your own business logic.
Background: symfony3
I have just stuck in the fact that redirectToRoute and addFlash methods in controller are protected in symfony. I have a separate class for action.
namespace AppBundle\Action;
class Base {
public function __construct($controller) {
$this->controller = $controller;
}
}
As you can see base action class requires a controller. Basically it is logical because action class is part of a controller and should have access to all its methods. However I cannot call $this->controller->addFlash as it is protected. If it is protected then there might be some reason for it. I cannot find it. Can you please hint me how I can change my action class so that it could use controller methods.
The variant about extending action from a controller does not fit me as I have additional functionality in the main controller. It is configured in a proper way.
Update: my goal is to devide controller functionality by responsibility. I invented an action class. My end code look like following:
public function editAction() {
$instance = new \AppBundle\Action\MyController\Edit($this);
return $insance->run();
}
In this case I keep controller clean and not verbose.
Here is a link to Symfony Controller Trait, that you can duplicate, if you really want to work that way.
But since you are injecting a whole symfony controller into your own controllers, you will be better off with extending instead. Injection is used here for injecting separate service by their IDs.
Using Symfony 2.7 and Doctrine 2.5, I have
an Interface Alsciende\MyBundle\Model\CycleInterface
an abstract class Alsciende\MyBundle\Entity\Cycle that implements the interface
a final class AppBundle\Entity\Cycle that extends the abstract class and implements the interface
a doctrine orm configuration with resolve_target_entities that maps the interface to the final class
This system works well and I was able to create the database and implements some CRUD in AppBundle, manipulating the target entity directly.
However, I now want to manipulate the target entity in MyBundle, through the Interface. I need to get its repository:
$this->getDoctrine()->getRepository('Alsciende\MyBundle\Model\CycleInterface');
But I get the exception
class 'Alsciende\MyBundle\Model\CycleInterface' does not exist
How can I get the repository of the target entity? That is, how can I call ResolveTargetEntityListener directly to get the name of the entity implementing the interface?
edit:
Why do I need that? Very simply, for example, I need a controller that displays a list of all Cycles. The interface defines that each Cycle has an id and a name. I want to display every Cycle with its name and id. In order to do that, I need to access the repository of the actual Cycle entities.
Alsciende/MyBundle/Model/CycleInterface.php
<?php
namespace Alsciende\MyBundle\Model;
interface CycleInterface
{
public function getId();
public function getName();
}
Alsciende/MyBundle/Controller/CycleController.php
<?php
namespace Alsciende\MyBundle\Controller;
class CycleController extends Controller
{
public function indexAction()
{
$cycles = $this
->getDoctrine()
->getRepository('Alsciende\MyBundle\Model\CycleInterface')
->findAll();
// return template with list $cycles
// using only id and name properties
}
}
It's the same way that FosUserBundle is able to manage the User entities, even though the User class defined in FosUserBundle is an abstract class.
How can I get the repository of the target entity?
In app/config/config.yml put:
doctrine:
orm:
resolve_target_entities:
Namespace\InterfaceInterface: Namespace\Entity\TargetEntityImplementing
BUT
Why do I need that? Very simply, for example, I need a controller that displays a list of all Cycles. The interface defines that each Cycle has an id and a name. I want to display every Cycle with its name and id. In order to do that, I need to access the repository of the actual Cycle entities.
It's not a solution in this case, IMO. I would rather used entity with #DiscriminatorColumn configured:
http://doctrine-orm.readthedocs.org/projects/doctrine-orm/en/latest/reference/inheritance-mapping.html#single-table-inheritance
Parent class will be some kind of interface you're looking for.
I recommend you to "merge" above: create a parent class which will implement such an interface, then map this interface to this class.
I have a big set of classes (like more that 100) and they are all extend from some abstract class, let's call it ParentClass. Let's call child classes ChildA,ChildB, etc. How can I register custom deserializer for all children and get class type inside my Deserializer?
I tried:
module.addDeserializer(ParentClass.class, new MyObjectDeserializer());
but it does not work.
I want to skip doing (what is working):
module.addDeserializer(ChildA.class, new MyObjectDeserializer(ChildA.class));
module.addDeserializer(ChildB.class, new MyObjectDeserializer(ChildB.class));
module.addDeserializer(ChildC.class, new MyObjectDeserializer(ChildC.class));
//etc......
Class type should be known, as I am use Jackson for spring #RequestBody method, what have defined class name there.
Any ideas how this can be done?
As far as I know, I don't think there is a mechanism in jackson that will address your exact needs.
However, there are a couple alternatives you can try.
Deserializing polymorphic types with Jackson describes one such alternative, however, you would still need to explicitly define all of the supported subtypes.
Another alternative that would not require you to explicitly define deserialization relationships would be to change your class hierarchy from one of inheritance to that of a container.
For example, converting your abstract parent class to a container like so:
public class DataContainer<T> {
String commonString;
Integer commonInteger;
T subData;
}
Would allow you to simply define in your controller input function as
public String controllerFunction(DataContainer<ClassA> classA);
without a need to define all these subclass deserializations.
Late to the party but I had a similar problem which I solved by registering a custom Deserializers to my SimpleModule. The code is in Kotlin but it should be easy to port it to Java.
The class itself:
class UseBaseClassSimpleDeserializers(
private val baseClass: Class<*>,
private val baseClassDeserializer: JsonDeserializer<*>
) : SimpleDeserializers() {
#Throws(JsonMappingException::class)
override fun findBeanDeserializer(
type: JavaType?,
config: DeserializationConfig?,
beanDesc: BeanDescription?
): JsonDeserializer<*>? {
val beanDeserializer = super.findBeanDeserializer(type, config, beanDesc)
return if (beanDeserializer == null && baseClass.isAssignableFrom(type!!.rawClass)) {
baseClassDeserializer
} else {
beanDeserializer
}
}
}
How to register the custom Deserializers class to a SimpleModule:
val simpleModule = SimpleModule()
simpleModule.setDeserializers(UseBaseClassSimpleDeserializers(ParentClass::class.java, ParentClassDeserializer()))
Even though I have specified the scope type as method, it gets instantiated in CONVERSATION scope.
>
UserHome userHome = (UserHome) Component.getInstance(UserHome.class, ScopeType.METHOD);
This is quite confusing, can someone explain this behavior?
When you call
Component.getInstance(UserHome.class, ScopeType.METHOD);
Seam internal behavior is to call
Object result = Contexts.lookupInStatefulContexts(name);
lookupInStatefulContexts API says
Search for a named attribute in all contexts, in the following order: method, event, page, conversation, session, business process, application.
As your ScopeType.METHOD does not contain your UserHome.class component, The search go on until get its scope (StypeType.CONVERSATION, right ?)
UPDATE
I was under the impression that if you specify the ScopeType to getInstance method you will be able to create the object within that scope
If the target component does not have the desired scope associated, getInstance method does not create the component within that scope. Instead it performs a hierarchical search by using Contexts.lookupInStatefulContexts till get some assigned scope
If you want more than one scope can be assigned to a component, you must scecify it by using #Role (#Roles) annotation
#Name("user")
#Scope(ScopeType.EVENT)
#Role(name="loggedUser", scope=ScopeType.SESSION)
public class User { ... }
So you specify the desired scope
Component.getInstance(User.class, ScopeType.EVENT);
or
Component.getInstance(User.class, ScopeType.SESSION);
remember Seam performs lookup by field/property name
private #In User user; // Take ScopeType.EVENT as scope
private #In User loggedUser; // Take ScopeType.SESSION as scope
I assume your UserHome class extends Seam's EntityHome class. The super class of EntityHome, which is Home, is in scope ScopeType.CONVERSATION:
#Scope(ScopeType.CONVERSATION)
public abstract class Home<T, E> extends MutableController<T>
Either you did not override the scope in your UserHome declaration or Seam ignores #Scope annotations in subclasses if one of the super classes already have an #Scope annotation.