I feel silly asking this question but I can't find a clear answer anywhere.
I have a token entity which has, among other things, a creation_time.
I have a parameter in parameters.yml called token_lifespan.
For a token, if creation_time < (time() - token_lifespan) then it has expired.
The problem is I can't find a nice way to pass the token_lifespan parameter to the entity. I know I could do something like:
$token->hasExpired($this->getParameter('token_lifespan'))
But that feels really icky. I've been looking at making a service, and dependency injection, as a way to pass the token lifespan to the token when it is created, but I can't work out what I'm doing.
I'm making my tokens with $token = new MyToken(); and I'm getting my tokens from doctrine like so:
$this->getDoctrine()
->getRepository('MyBundle:MyToken')
->find($token_id);
Do I need to make my repository a service? Something like:
mytoken_repository:
class: MyBundle\Entity\MyToken
factory_service: 'doctrine.orm.default_entity_manager'
factory_method: 'getRepository'
calls:
- [setLifespan, ['%token_lifespan%']]
And what exactly does this mean? Do I still create tokens the same way, with new MyToken()? and just make a setLifespan method in the MyToken class to store the lifespan?
Finally, can I still get tokens back from Doctrine with $this->getDoctrine()...? and what if I need to use the token_lifespan parameter in my repository class?
Sorry there's about a million questions in there. I feel like the dependency injection part of the Symfony docs assumes I know a lot more about the framework than I currently do.
Let's focus on this:
I've been looking at making a service, and dependency injection, as a way to pass the token lifespan to the token when it is created, but I can't work out what I'm doing.
I will strongly recommend you using Event Listener, prePersist in particular. Why? Simply, if you pass your lifespan to the event listener, and there you calculate your expiration date, you can set that date to your entity and use it later, when needed. Quote from Doctrine documentation about prePersist events:
prePersist - The prePersist event occurs for a given entity before the respective EntityManager persist operation for that entity is executed. It should be noted that this event is only triggered on initial persist of an entity (i.e. it does not trigger on future updates).
So, what do you need to do? Register the listener class first:
token.listener:
class: MyBundle\EventListener\TokenListener
arguments:
- %token_lifespan%
tags:
- { name: doctrine.event_listener, event: prePersist }
With this, you can easily pass your token_lifespan as a constructor to your class.
Then, you need to create the class itself:
namespace MyBundle\EventListener;
use Doctrine\ORM\Event\LifecycleEventArgs;
use MyBundle\Entity\Token;
class TokenListener
{
/**
* #var int
**/
private $token_lifespan = null;
public function __construct($tokenLifespan) {
$this->token_lifespan = $tokenLifespan;
}
public function postPersist(LifecycleEventArgs $args)
{
$entity = $args->getEntity();
$entityManager = $args->getEntityManager();
// Do your magic here, only if our class is of type Token
if ($entity instanceof Token) {
// Calculate your expiration date here using $this->token_lifespan
// and once you're done, set the result to the entity
$entity->setExpirationDate(....);
}
}
}
I can explain why creating Repository as a service would not work for you, if you like. But anyway, give this a try.
You should get the logic out of the entity and make a service with it. An entity should not have any dependencies.
You configure the service to have %token_lifespan% as an argument and make a method like this:
/**
* #return boolean
*/
function isExpired(Token $token) {
//your logic here, add dependencies to constructor and "arguments:" in service configuration
}
I hope this gets you started.
Related
How does one test that events were dispatched during a function call?
public function updateUser() {
//Do some update stuff
$event = new UserUpdated($user);
$event->attach([
new SendEmailAddressChangeEmail($emailAddress),
new SendEmailAddressChangeEmail($oldEmailAddress),
]);
$event->dispatch();
}
Aside from setting up an email address and seeing if an email is sent, how can I check (using PHP Unit) that the dispatcher is actually dispatching these events? I am assuming that I need to create a mock of some sort, but I am uncertain how to create a mock for a completely unrelated bit of code.
UserUpdated Event code:
class UserUpdated extends BaseEvent
{
public $user;
public function __construct(User $user) {
$this->user = $user;
}
}
and the related SendEmailAddressChanged Handler code:
class SendEmailAddressChangeEmail implements Contracts\HandlerInterface
{
protected $emailAddress;
public function __construct($emailAddress) {
$this->emailAddress = $emailAddress;
}
public function handle($event) {
EmailUtils::sendEmailAddressChangeEmail($this->emailAddress, $event->user->userName, $event->user->userID);
}
}
The updateUser() method you've got does two things in one that especially does not work well with (unit) testing:
business logic
object creation
From your own sense of things I assume this is also what made you ask this question. Often code that is not straight forward to test also is a good canary for design issues, so it is normally best to tackle w/ it.
These two points (1. and 2.) are an over-simplification of what is borrowed from the "Two piles" outlined by Misko Hevery in far more detail in his Clean Code Talks:
For example in "The Clean Code Talks -- Inheritance, Polymorphism, & Testing" from Nov 2008 - https://youtu.be/4F72VULWFvc?t=1328 ("Two Piles" # 22:08)
One solution to make this code more test-able is the use of dependency injection. That is one factory (method) for the user-event and one factory (method) for the object updateUser() is a method of. That concrete type then can make use of the factory object it gets injected to obtain the even object.
In short: If that update-user object needs a user-updated-event object it needs to ask for it in it's constructor.
As you sometimes don't want to create that user-updated-event object beforehand, the alternative is inject an object that knows how to create that user-updated-event object, these kind of objects are called factories.
The test then can inject a factory that presents an event mock object with the expectation that it is dispatched.
A good dispatch library btw. does already provide ready-made mocks for tests but that is out of the scope of Phpunit.
If you don't know yet about the mock functionality of Phpunit, please checkout the product's documentation for it:
Phpunit 7.1 Docs » 9. Test Doubles
In Laravel's Container Illuminate\Container\Container, in the bind method, we see this bit of code:
// If the abstract type was already resolved in this container we'll fire the
// rebound listener so that any objects which have already gotten resolved
// can have their copy of the object updated via the listener callbacks.
if ($this->resolved($abstract)) {
$this->rebound($abstract);
}
It appears to allow to rebinding of any previously resolved abstract type. In what use cases would you need to rebind an abstract type? In the rebound method, we see that if indeed the abstract type was previously resolved, we run all the rebound callbacks.
/**
* Fire the "rebound" callbacks for the given abstract type.
*
* #param string $abstract
* #return void
*/
protected function rebound($abstract)
{
$instance = $this->make($abstract);
foreach ($this->getReboundCallbacks($abstract) as $callback) {
call_user_func($callback, $this, $instance);
}
}
Can someone give an real-life example as to what type of situation you would rebind an abstract type, and what type of callbacks you would need to call?
This mechanism allows you to define how any implementations registered in the future should be initialized.
For example: Imagine that you had a RPC client that connects to some server based on configuration files. And you would want any client code to be able to extend your Client class and register their own implementations, but without burdening them with having to initialize the client with the input from the settings file manually. (By "client code" we mean any code that would use your library, the term "client" is confusing here.)
(I wanted to do a similar thing in my Laravel project because I wanted to define the path to my config (the rpc.client) only once and not every time that I register different Client implementation. I did not want the rest of the application to care about such things).
Example implementation:
class CommunicationsServiceProvider extends ServiceProvider
{
/**
* Register the application services.
*
* #return void
*/
public function register()
{
$this->app->singleton(ClientInterface::class, function(Application $app)
{
return null; // Required to trigger the rebinding event with the second bind.
});
$this->app->rebinding(ClientInterface::class, function(Application $app, $client)
{
$options = $app['config']->get('rpc.client');
/** #var Client $client */
$client->configure($options);
return $client;
});
$this->app->singleton(ClientInterface::class, function(Application $app)
{
$client = new Client ([]); // We intentionally use empty array to demonstrate
// that the config will be properly injected anyway.
return $client;
});
// Note that the callback in the binding() method will be called, so even though
// we initialize the new Client with empty set of settings (we provide the [] into
// the constructor), it will be properly initialized by the rebinding() callback.
So every time anyone registers a new Client implementation (via App::bind() etc.) the callback defined in rebinding() will be called on the instance.
Conclusion
What I wonder about thought is whether it is a good idea to use the rebinding() method at all when it is not documented in Laravel docs and is not part of the Container contract (the Illuminate\Contracts\Container\Container interface) and is only a part of the concrete implementation (the Illuminate\Container\Container class ).
It seems that for the same usecase you could use the "proper" (documented and present in public API) resolving() method, which will cause the configure($options) method to be called every time someone resolves this binding (contrary to this case where it is only called once—when the Client is registered).
Sources
I have first noted this functionality in this tutorial, but honestly I did not properly understand it from the example there and had to experiment with it on my own.
Disclaimer
Please note that I am by no stretches of imagination an expert on this topic. But I have currently been looking into the Laravel IoC and this method so I thought that I might provide some insight.
I'm creating symfony2 application with doctrine2 and I would like to ask for advice regarding common/good practice for DTO-Entity, Entity-DTO conversion. I've found some information for all languages and frameworks, but none for SF2.
I would like to isolate Entities, so they are used only in Services and DAO's (Managers, Repositories in SF2 terminology). Controllers won't ever see DAO's or Entities and will interact with business logic only via Services. All communication between Services and Controllers should be done via primitive types, scalars, DTO's.
Example :
Controller
class RegistrationController extends Controller
{
public function registerAction($name)
{
$userDTO = new UserDTO();
$form = $this->createForm(new UserType(), $userDTO);
$form->handleRequest($request);
if ($form->isValid()) {
$userService = $this->get('userService');
$userService->createUser($userDTO);
return $this->redirect($this->generateUrl('success'));
}
--//--
}
}
Service
class UserServiceImpl implements UserService
{
private $userDao;
public function __construct(UserDao $userDao)
{
$this->userDao = $userDao;
}
public function createUser(UserDTO $user)
{
$user = new User(); #doctrine entity
$user->setFirstName($userDTO->getFirstName());
$user->setLastName($userDTO->getLastName());
$this->userDao->persist($user);
$this->userDao->flush();
--//--
}
}
Problem quickly appears with rising amount of properties in User object. In my application User has 13 fields. Are there any SF2 tools (classes) to simplify this process ? Do you write your own convertors / transformers ? Could you please show example of how it should look like ? Maby PHP magic methods could help ? What about reflection ?
Thanks for advices and opinions.
Start by using public properties on your dto's. That eliminates a bunch of getter/setter methods which really should not do anything for dto's. You can always add some majic methods for special cases.
Next, rethink the design of your DoctrineUserEntity aka Domain object. Do you really need getter/setter for each attribute? If so then what's the point?
Instead try to group properties into value objects:
$userNameValueObject = new UserNameValueObject($userDto->firstName, $userDto->lastName);
$userEntity = new UserEntity($userDTO->username,$userDTO->password, $userNameValueObject);
// And maybe this for updates
$userEntity->updateName($userNameValueObject);
But again, make sure you are actually getting some value for your work. A bunch of one to one mappings might make sense on other platforms where domain objects can stay alive between request. In php, everything starts from ground zero.
One option I've recently found is https://github.com/jasonrobertfox/DTOx which is a generator for DTO's and tests. It does the annoying boiler plate generation work for you.
Imagine we have a Request object and a Controller object. The Controller object is constructed with a Request object, like so:
abstract class Controller {
public $request;
public function __construct(Request $request)
{
$this->request = $request;
}
}
As you can see, this is an abstract class, so in reality a subclass of Controller will be constructed. Let's imagine the code is something like this:
// Instantiate the request.
$request = new Request($params);
// Instantiate the registration controller.
$controller = new RegistrationController($request);
Now let's say that we add a dependency to our RegistrationController, like so:
class RegistrationController extends Controller {
private $user_repo;
public function __construct(Request $request, UserRepo $user_repo)
{
parent::__construct($request);
$this->user_repo = $user_repo;
}
}
At this point, what I'd like to do is introduce a dependency injection container to automatically inject the dependencies via the constructor. For this, I've been using PHP-DI. Usually, this would go something like so:
// Instantiate the registration controller.
$controller = $container->get('RegistrationController');
This would then instantiate RegistrationController with an instance of Request and an instance of UserRepo. It'd know to construct with those objects thanks to reflection, but if I wanted I could also override this via a configuration file.
The problem is that the Request object takes a parameter which is dynamic. I need the Request object passed to RegistrationController to be a specific instance, one I've just created.
I essentially want to be able to say: "Give me an instance of this class with all of its dependencies injected, but for a particular parameter, pass in this specific instance".
I've looked to see if PHP-DI (and a hand-full of other DI containers) support this kind of "override" for specific parameters, but so far I can't find anything.
What I want to know is:
Is there a DI container out there that can do this?
Is there an alternative approach which would leave the classes clean (I don't want to use annotations or anything else that'll add the container I use as a dependency)?
PHP-DI author here.
So there are two things, first I'll answer your question:
PHP-DI's container provides a make method that you can use like that:
$request = new Request($myParameters);
$controller = $container->make('RegistrationController', array(
'request' => $request
));
This make method, as you can see, is the same as get except it will always create a new instance (which is what you want here since you probably don't want to reuse an existing instance of the controller) and it will take the extra parameters you give it. That's the behavior of a factory, with the benefits of the container that will find the rest of the parameters you didn't provide.
So that's what I would use here. You could also do this:
$request = new Request($myParameters);
$container->set('Request', $request);
$controller = $container->get('RegistrationController');
But that's less clean because it will set the request in the container, which is bad (explained below).
Now the second thing is that a request object is not really a service, it's a "value object". A container should generally only contain service objects, i.e. objects that are stateless.
The reason for this is imagine you have several request in the same process (e.g. you do "sub-requests", or you have a worker process that handles several requests, etc...): your services would be all messed up because they would have the request injected and the request object might change.
Symfony did just that and realized it was a mistake. Since Symfony 2.4, they have deprecated having the Request in the container: http://symfony.com/blog/new-in-symfony-2-4-the-request-stack
Anyway, so what I suggest you to do is not to have the Request object in the container, but instead use the make method I showed you.
Or, even better, I would do that:
class RegistrationController extends Controller {
private $user_repo;
public function __construct(UserRepo $user_repo)
{
$this->user_repo = $user_repo;
}
public function userListAction(Request $request)
{
// ...
}
}
// in the front controller
$controller = $container->make('RegistrationController');
// This is what the router should do:
$action = ... // e.g. 'userListAction'
$controller->$action(new Request($myParameters));
(this is what Symfony and other frameworks do by the way)
I need to check if a persisted entity has changed and needs to be updated on the database.
What I made (and did not work) was the following:
$product = $entityManager->getRepository('Product')->find(3);
$product->setName('A different name');
var_export($entityManager->getUnitOfWork()->isScheduledForUpdate($product));
That code prints always false, I also tried to flush before check the unit of work, but did not work.
Anyone has a suggestion?
The first thing I'd check it that your setName function is actually doing something ($this-> name = $name...) If it's already working, then you could define an event listener on your services.yml that is triggered when you call the flush.
entity.listener:
class: YourName\YourBundle\EventListener\EntityListener
calls:
- [setContainer, ["#service_container"]]
tags:
- { name: doctrine.event_listener, event: onFlush }
Then you define the EntityListener
namespace YourName\YourBundle\EventListener;
use Doctrine\ORM\Event;
use Symfony\Component\DependencyInjection\ContainerAware;
class EntityListener extends ContainerAware
{
/**
* Gets all the entities to flush
*
* #param Event\OnFlushEventArgs $eventArgs Event args
*/
public function onFlush(Event\OnFlushEventArgs $eventArgs)
{
$em = $eventArgs->getEntityManager();
$uow = $em->getUnitOfWork();
//Insertions
foreach ($uow->getScheduledEntityInsertions() as $entity) {
# your code here for the inserted entities
}
//Updates
foreach ($uow->getScheduledEntityUpdates() as $entity) {
# your code here for the updated entities
}
//Deletions
foreach ($uow->getScheduledEntityDeletions() as $entity) {
# your code here for the deleted entities
}
}
}
If you need to know which entities are being changed, but do something with them after they've been saved to the database, just store the entities changed in a private array, an then define a onFlush event that gets the entities from the array.
BTW, to trigger this kind of events you need to add the #ORM\HasLifecycleCallbacks on the entity.
I didn't need/want to create Listeners for my case so I ended up with
$product->setName('A different name');
$uow = $entityManager->getUnitOfWork();
$uow->computeChangeSets();
if ($uow->isEntityScheduled($product)) {
// My entity has changed
}
Doctrine2 Docs. 17. Change Tracking Policies
If you use third form (17.3. Notify) as i do, you can test if your entity is changed doing:
$uow = $entityManager->getUnitOfWork();
$uow->computeChangeSets();
$aChangeSet = $uow->getEntityChangeSet($oEntity);
If nothing changed it will return blank array.
You may also want to look at the PreUpdate event, if you need access to entity fields with their old and new values.
A bit of an example mostly taken from the link provided:
<?php
class NeverAliceOnlyBobListener
{
public function preUpdate(PreUpdateEventArgs $eventArgs)
{
if ($eventArgs->getEntity() instanceof User) {
if ($eventArgs->hasChangedField('name') && $eventArgs->getNewValue('name') == 'Alice') {
$oldValue = $eventArgs->getOldValue('name');
$eventArgs->setNewValue('name', 'Bob');
}
}
}
}
If you only need to compare old and new state of object then probably this would be simpler:
$originalEntityData = $entityManager->getUnitOfWork()->getOriginalEntityData($entityObject);
The issue is quite old but there may be still some group of people that might face this problem from a different point of view.
The UnitOfWork works great but it only returns the array of changes. It can be a pain in butt when someone doesn't actually knows which fields may have changed and just wants to get the whole entity as an object to compare $oldEntity and $newEntity. Even though the event's name is preUpdate if someone will try to fetch the data from the database as follows:
$er->find($id);
the returned entity will contain all changes.
The workaround is quite simple but it has some hooks:
public function preUpdate(Entity $entity, PreUpdateEventArgs $args)
{
$entity = clone $entity; //as Doctrine under the hood
//uses reference to manage entities you might want
//to work on the entity copy. Otherwise,
//the below refresh($entity) will affect both
//old and new entity.
$em = $args->getEntityManager();
$currentEntity = $em->getRepository('AppBundle:Entity')->find($entity->getId());
$em->refresh($currentEntity);
}
For those who are using another event, like preFlush, I've quickly checked it and the workaround didn't work well because probably the refresh() method discards any flush changes so what needs to be done is to call the flush once again in listener and create some static $alreadyFlushed toggle to avoid circular reference.
Based on my needs, answers here and the docs, I came up with the following solution for a modifiedAt timestamp in an Entity.
/**
* #Doctrine\ORM\Mapping\PreUpdate()
*
* #param \Doctrine\ORM\Event\PreUpdateEventArgs $args
* #return $this
*/
public function preUpdateModifiedAt(\Doctrine\ORM\Event\PreUpdateEventArgs $args)
{
$this->setModifiedAt(new \DateTime('now'));
return $this;
}
This is based on what the docs say about this Event as opposed to the other available ones, such as PostPersist and PreFlush:
PreUpdate is the most restrictive to use event, since it is called
right before an update statement is called for an entity inside the
EntityManager#flush() method. Note that this event is not triggered
when the computed changeset is empty.
Using PreUpdate as opposed to the others lets you leave all the computations and calculation intensive functions to the process already defined by Doctrine. Manually triggering computation of changesets, such as in these answers above are server CPU intensive. The onFlush Event, such as used in the accepted answer is an option (in the way demonstrated), but not if you rely on detecting a change to the Entity, as you can with the function above (preUpdateModifiedAt(PreUpdateEventArgs $args)).
I agree with #Andrew Atkinson when he said:
You may also want to look at the PreUpdate event, if you need
access to entity fields with their old and new values.
But I disagree with the example he proposed, from my experience, there is a better way to check if something changed or not.
<?php
class Spock
{
public function preUpdate(PreUpdateEventArgs $eventArgs)
{
if (!empty($eventArgs->getEntityChangeSet())) {
// fill this how you see fit
}
}
}
This way the if will only be triggered if there is really some field that changed or not.
As to how to do it if this or that field was changed, then yeah, I recommend his solution.
I am curious about Doctrine and everyone documenting postFlush, as in some case, you have an ongoing transaction.
I'd like to point out there's also postTransactionCommit, which could be safer depending on what you're trying to achieve in the postFlush event.