Here's sample code:
class UsersController extends AppController
{
...
public function implementedEvents()
{
return [
'Auth.logout' => 'afterLogout'
];
}
public function afterLogout($event)
{
$this->Flash->toast(__('Good bye!'));
}
...
}
Before implementing implementedEvents() method, AppController::beforeRender() method was triggered properly.
I needed to listen to Auth.logout event, so wrote implementedEvents() method. I thought it would be merged to the default events array. But after that, AppController::beforeRender() stopped working. It didn't trigger any more. So I guess there's an overwriting behavior.
Is this a default behavior of CakePHP 3? And is this an intended behavior or a bug?
That's the intended behavior, as otherwise it would be complicated to overwrite instead of merging.
If you need to merge possible parent listener configuration, then you need to do that on your own, like
public function implementedEvents()
{
return [
'Auth.logout' => 'afterLogout'
] + parent::implementedEvents();
}
Related
I have a Laravel app which has an object, Position, which is created via a form.
class Position extends Model
{
protected $dispatchesEvents = [
'creating' => PositionCreating::class,
];
And this calls an event of the PositionCreating class, which I've tested, and is correctly firing. The underlying code also works to give me success or fail criteria.
class PositionCreating
{
use Dispatchable, InteractsWithSockets, SerializesModels;
public function __construct(Position $position)
{
if (some_good_stuff())
{
//keep creating the object
} else {
//stop creating the object
}
}
If it works, that's fine, I just let the __construct() function finish executing and everything, including the pre-execution code I want, works perfectly.
But I don't know how to actually stop the creation of the object. I can, of course use the dd() function or something (which works and stops creation of the object as expected), but I want to present a readable error to the user in a friendly manner. What function or commands should I be using to cancel the creation of the object to return back to my position.create method?
A bit late answer but this is a way to do it. Models fire several events. The one you're looking for is probably the "created" event. Each model event receive an instance of the model so you could just attach an event on your model, just like this:
protected $dispatchesEvents = [
'created' => PositionCreated::class,
];
Inside your "PositionCreated" event, add a public property to get the model instance,like this:
public $position;
public function __construct(Position $position)
{
$this->position=$position;
}
Finally just add the logic on your "handle" method inside your event listener.
public function handle($event)
{
if($something)
{
$event->position->delete();
}
}
This should do the work.You can check for the other events and see wich one suits you the most.
I'm trying to understand how to use the laravel 5.0 command bus, but I am struggling to get it right, so I'm looking for help with a few questions:
I'd like to dispatch an array of memberIds to run through a loop inside the handle function of the command.
$members:
array:2 [
0 => "147"
1 => "148"
]
This array is sent like this:
$this->dispatch(new SendMail($members));
How do I access the array in the handle method of SendMail command? I haven't found many examples, but nearly all of them pass $command into the handler.
public function handle($command)
{
//this doesn't work
foreach($command->members as $memberId){
$contractor = Contractor::find($memberId);
}
Do I need to return anything from the handler in order to continue running the other logic inside of my original function?
Since your trying to inject your $members array to your job's constructor method, you need to handle it there.
Then you'll be able to use your array in the hanle method.
// in your job class
protected $members;
public function __construct($members)
{
$this->members = $members
}
public function handle ()
{
foreach ($this->members as $member) {
//your logic
}
}
And if you want to inject an Eloquent model to your job (instead of an array) remember that you can typehint directtly in the constructor and , using the SerializesModels trait Laravel will retrieve it for you. This is described in the documentation.
I have a problem. I try to get the Entity-Manager without a Controller, but I found no way.
At this time, I get the Entity-Manager like this:
(Controller)
public function getEntityManager()
{
if (null === $this->_em) {
$this->_em = $this->getServiceLocator()->get('Doctrine\ORM\EntityManager');
}
return $this->_em;
}
(Plugin)
public function getEntityManager()
{
if($this->_em == null){
$this->_em = $this->getController()->getServiceLocator()->get('doctrine.entitymanager.orm_default');
}
return $this->_em;
}
You see, I need allways a controller. But, if I need the EntityManager in a model, i have a problem. I can give the model the controller, but I think this is really a bad way.
Have you any idea to get the EntityManager without a controller?
The way I handle Doctrine is through Services, i do it like the following:
//some Controller
public function someAction()
{
$service = $this->getServiceLocator()->get('my_entity_service');
return new ViewModel(array(
'entities' => $service->findAll()
));
}
The Service->findAll() would look something like this:
public function findAll()
{
return $this->getEntityRepository()->findAll();
}
Now we need to define the my_entity_service. I do this inside my Module.php
public function getServiceConfig()
{
return array(
'factories' => array(
'my_entity_service' => 'Namespace\Factory\MyServiceFactory'
)
);
}
Next I create the Factory (note: this could also be done via anonymous function inside the Module.php)
<?php
namespace Namespace\Factory;
use Zend\ServiceManager\ServiceLocatorInterface;
use Zend\ServiceManager\FactoryInterface;
use Namespace\Model\MyModel;
class MyServiceFactory implements FactoryInterface
{
/**
* Create service
*
* #param ServiceLocatorInterface $serviceLocator
* #return mixed
*/
public function createService(ServiceLocatorInterface $serviceLocator)
{
$myModel= new MyModel();
$myModel->setEntityManager($serviceLocator->get('Doctrine\ORM\EntityManager'));
return $myModel;
}
}
Now this is a lot to chew :D I totally get that. What is happening here is actually very simple though. Instead of creating your model and somehow get to the EntityManager, you call ZF2's ServiceManager to create the Model for you and inject the EntityManager into it.
If this is still confusing to you, I'll gladly try to explain myself better. For further clarification however I'd like to know about your use case. I.e.: what for do you need the EntityManager or where exactly do u need it.
This code example is outside of the question scope
Just to give you a live example of something I do via ServiceFactories with forms:
public function createService(ServiceLocatorInterface $serviceLocator)
{
$form = new ReferenzwertForm();
$form->setHydrator(new DoctrineEntity($serviceLocator->get('Doctrine\ORM\EntityManager')))
->setObject(new Referenzwert())
->setInputFilter(new ReferenzwertFilter())
->setAttribute('method', 'post');
return $form;
}
Your real question is "How to get an Instance of ServiceManager in my own classes"
For this, take a look at the docu: (bottom of page http://zf2.readthedocs.org/en/latest/modules/zend.service-manager.quick-start.html)
By default, the Zend Framework MVC registers an initializer that will
inject the ServiceManager instance, which is an implementation of
Zend\ServiceManager\ServiceLocatorInterface, into any class
implementing Zend\ServiceManager\ServiceLocatorAwareInterface. A
simple implementation looks like the following.
so implent the ServiceLocatorInterface in your classes and then inside your class you can call:
$this->getServiceLocator()->get('doctrine.entitymanager.orm_default');
or any other service you have registered.
I have a web site I'm converting to Codeigniter and I want to simplify and decouple. I like what I've read about the Observer pattern for things like "new survey created" (which triggers a new help ticket, which triggers an email, etc).
But how do I implement such a thing in Code Igniter? I see the Symfony component but at this point I'm not concerned about understanding the system as much as figuring out how to use it in controllers and models. I have extended both CI_Model and CI_Controller already for other reasons. Would putting Observer pattern code there be the best?
I imagine a point like this: someone hits the web site and spawns a request which gets routed to a controller/action: http://localhost/test/save_changes
// warning, pseudo-code!
class Test extends MY_Model
{
public function __construct ()
{
// do I put this here?!? - or maybe in MY_Model?
// Should it be a singleton?
$this->load->library('dispatcher');
// where do I attach what I want... here?
$this->load->library('emailer');
$this->dispatcher->attach($this->emailer);
// what if I have 50 possible things that might happen
// based on any given event, from adding a user to
// deleting a survey or document? There has got to be a
// way to attach a bunch of observers that trickle
// down to each object, right?
}
public function save_changes ()
{
$this->load->model('user');
$this->user->init($this->session->userdata('user.id'))->save();
}
}
class User extends MY_Model
{
public function __construct ()
{
parent::__construct ();
// do I put this here?!?
$this->load->library('dispatcher'); // just something to call it
}
public function init($id)
{
if($this->_loadUser ($id))
{
$this->dispatcher->notify($this, 'user.loaded');
}
}
public function save($id)
{
if(parent::save())
{
$this->dispatcher->notify($this, 'user.saved');
}
}
}
class Emailer
{
public function update ($caller,$msg)
{
switch ($msg)
{
case 'user.saved':
// send user an email
// re-cache some stuff
// other things that we might want to do, including more of these:
$this->dispatch->notify('user-saved-email-sent');
break;
}
}
}
class Dispatcher
{
public function notify ($caller, $msg) { ...foreach attached do $obj->update($caller,$msg) ...}
public function attach ($obj) { ... }
public function detach ($obj) { ... }
}
I can see how powerful that would be. But I'm not sure how to simplify the setup and attaching of all of these listener/observers.
Maybe I should have a factory to create them all? It just seems like yes, they would be decoupled from the way it currently works, but it seems managing all the different objects that I'd have to 'attached' in each controller or method would become coupled in a different way.
Thanks,
Hans
Your proposed structure would have to be something like:
$this->load->library('observer_factory', 'of'); // factory for creating observers
// Observer_factory would have knowledge/access to different classes which relate
// to the pattern.
$ync = $this->of->getNotifier( $some_variable ) );
$ync->attach( $this->of->getObserver( $some_other_variable ) );
$ync->attach( $this->of->getObserver( $some_final_variable ) );
$ync->someMethod(); // someMethod calls notify
But I wonder about it. You'd have a factory class that slowly becomes all-knowing. It starts usurping the functionality of the Loader. Why load a library when my Observer_factory can handle it by doing exactly the same thing?
I think you're better off with a library or a model that knows what it is supposed to do and is well designed, then adding this class structure. I do not see the gains outweighing the costs.
I have a SymfonyForm which has 1:n embedForm(s). The main form and the embedForm class got their own PreValidation, which implements a conditional validation.
A part of the EmbedForm class looks like this:
private function configurePreValidators() {
$validator = new sfValidatorCallback( array('callback'=> array($this, 'preValidation')) );
$this->getValidatorSchema()->setPreValidator(new sfValidatorOr( array( $validator ) ));
}
public function preValidation(sfValidatorCallback $validator, array $values){
...
$this->getValidator(self::SOME_FIELD)->setOption('required', false);
...
}
public function configure() {
...
$this->configurePreValidators();
parent::configure();
}
The prevalidation of the main form is similar.
When I submit the form, the main form prevalidation works fine.
In the embed-Form the "SOME_FIELD" gets a required-validation-error although I set it explicit to setOption('required', false) in the preValidation of the embedForm.
Is there something what I have to consider when I use pre-validation in an embedForm? What about mergePreValidator? Any hints about that?
Thanks in advance!
The issue here is not that your pre and post validators aren't firing -- they are (or at least, they should be). The issue is that the validator you are modifying is preValidate is not the one referenced in the top-level validator schema, i.e. the validator schema for the top-level form.
One solution: rather than modify the validator in preValidate, simply perform the validation:
public function preValidation(sfValidatorCallback $validator, array $values)
{
if (!$this->getValidator(self::SOME_FIELD)->isEmpty($values[self::SOME_FIELD])
{
throw new sfValidatorErrorSchema(self::SOME_FIELD => new sfValdiatorError($validator, 'msg'));
}
}
Note, this solution has some danger: if you modify the validator for SOME_FIELD inside of the top-level form, it will not be modified in this pre validator and vice-versa.
Let's look at why. In sfForm::embedForm:
public function embedForm($name, sfForm $form, $decorator = null)
{
...
$this->validatorSchema[$name] = $form->getValidatorSchema();
...
}
Symfony simply nests the validators. This is why pre and post still get called. Why does the reference change? sfValidatorSchema::offsetSet:
public function offsetSet($name, $validator)
{
...
$this->fields[$name] = clone $validator;
}
So when a form is embedded, the validator schema is cloned. Thus, any modifications to the validators inside of an embedded form do not affect the top-level validator schema.