Doctrine 2 Events - doctrine-orm

I'm trying to setup an event trigger in Doctrine, so that i can have access to the querybuilder done atm.
I've setup in my module the listener:
$doctrineEventManager = $e->getApplication()->getServiceManager()->get('doctrine.eventmanager.default_orm');
$doctrineEventManager->addEventListener('example', function($eventArgs) {
die("event");
});
And in my repository:
$this->getEntityManager()->getEventManager()->dispatchEvent('example', $query);
But it seems i'm doing something wrong or missing somehting, because its not working..
I cant find a way to implement this simple routine..
Thanks.

You must define a class with the event function you need, so that's called automatically.
$doctrineEventManager->addEventListener('example', new ExampleEvent());
Then, create ExampleEvent class and add "example" function:
<?php
class ExampleEvent {
public function example($eventArgs) {
// do whatever you want..
}
}
After, you just need to trigger the event, in your repo:
$this->getEntityManager()->getEventManager()->dispatchEvent('example', $eventArgs);
I did not try, but it will be something like this, in a nutshell.

Related

Why isn't my EventAggregator Subscription Handling This Event?

I have an Autofac DI Container defined as follows:
public class Bootstrapper
{
public IContainer BootStrap()
{
var builder = new ContainerBuilder();
builder.RegisterType<ItemViewModel>().AsSelf();
builder.RegisterType<EventAggregator>()
.As<IEventAggregator>()
.SingleInstance();
}
}
I have a Unit Test defined to test whether a deletion removes the deleted item from the collection:
[Fact]
public void Should_remove_item_from_collection_when_item_is_deleted()
{
const int deletedId = 42;
// adds three items to the collection
_openItemEditViewEvent.Publish(deletedId);
_openItemEditViewEvent.Publish(8);
_openItemEditViewEvent.Publish(9);
// I've tried this:
_eventAggregatorMock.Object.GetEvent<ItemDeletedEvent>().Publish(42);
// and alternatively, this (not at the same time):
_itemDeletedEventMock.Object.Publish(42);
Assert.Equal(2,_vm.ItemEditViewModels.Count); // always fails
Assert.False(_vm.ItemEditViewModels
.Select(vm => vm.Item.Id).Contains(42), "Wrong item deleted");
}
The constructor of the Unit Test initializes and assigns the EventAggregator to the view model:
_eventAggregatorMock = new Mock<IEventAggregator>();
_itemDeletedEventMock = new Mock<ItemDeletedEvent>();
_eventAggregatorMock.Setup(ea => ea.GetEvent<ItemDeletedEvent>())
.Returns(_itemDeletedEventMock.Object);
_vm = new ItemViewModel(_eventAggregatorMock.Object, */ ... /*);
In my actual view model, I Subscribe to the event:
public ItemViewModel(IEventAggregator ea, /* ... */)
{
_eventAggregator.GetEvent<ItemDeletedEvent>()
.Subscribe(OnItemDeleted, true);
}
And we never hit a breakpoint here:
public void OnItemDeleted()
{
// never happens
}
For the life of me, I can't figure out what I'm doing wrong - I'm overlooking something... do I have to Setup the event's Publish event in the Mock? Should I be using a real ItemDeletedEvent instance instead of a Mock? Any help would be greatly appreciated.
=> Hi Scott,
there are 2 ViewModel-scenarios you want to test when using an EventAggregator:
You want to test that your ViewModel is publishing an event
You want to test that your ViewModel does something when an event was published. So the ViewModel has to subscribe to that Event to do something
(Note: The following lines are true for PRISM's EventAggregator, which is the one you're using I guess. For other EventAggregators it could be different)
For the first scenario, you have to create a mock for the event. Then you can verify on that mock-instance that the Publish-method of the Event has been called.
For the second scenario, which is the scenario you have in your question, you have to use the real event in your test. Why?
When you call the Publish-method on a event-mock, that Publish method won't call the subscribers to that Event, as there's no logic behind the Subscribe-method. For sure you could setup both methods and implement that publish/subscribe-logic in your mock. But there's no reason to do so, just use the real Event
When you use the real event, the Publish-method will call all the subscribers. And this is exactly What you need in your test.
It should look like this:
_itemDeletedEvent = new ItemDeletedEvent();
_eventAggregatorMock.Setup(ea => ea.GetEvent<ItemDeletedEvent>())
.Returns(_itemDeletedEvent);
Now your ViewModel will get this itemDeletedEvent-instance from the EventAggregator. In your test you call the Publish-method on this itemDeletedEvent-instance and it will work.
More about this is explained in my Course on Pluralsight about WPF and Test Driven Development: http://www.pluralsight.com/courses/wpf-mvvm-test-driven-development-viewmodels
Thomas
http://www.thomasclaudiushuber.com

EmberJS: How to check if a component exists (inside another component)?

My goal is to resolve to a generic component is the component doesn't exists. It managed to do it like this:
// app/components/dynamic-widget.js
...
widgetName: function() {
var name = this.get('config.name');
if (!this.container.resolve('component:'+name)) {
name = 'generic-widget';
}
return name;
}.property('config.name')
...
Then in app/templates/components/dynamic-widget.hbs:
{{component widgetName}}
Then, I could use my dynamic-component like this:
{{dynamic-widget 'foo-widget'}}
If foo-widget is not implemented, it fallback into generic-widget.
But since EmberJS 1.11, resolving a component from a component's container is deprecated:
DEPRECATION: resolve should be called on the registry instead of the container
So my question is, how can I check if a component actually exists without using this.container.resolve ?
Thanks a lot.
Does this.container.registery.resolve work?
Looking at the code in git https://github.com/emberjs/ember.js/blob/5fd2d035b30aa9ebfe73de824b3b283ec8e589cc/packages/container/lib/registry.js
Looks like you may also be able to use this.container.registery.has

Issue testing Laravel Controller with Mockery | trying to get property of non-object

I'm very new to testing controllers and I'm running into a problem with a method(). I believe I'm either missing something in my test or my Controller / Repository is designed incorrectly.
The application I'm writing is basically one of those secure "one time" tools. Where you create a note, the system provides you with a URL, once that url is retrieved the note is deleted. I actually have the application written but I am going back to write tests for practice (I know that's backwards).
My Controller:
use OneTimeNote\Repositories\NoteRepositoryInterface as Note;
class NoteController extends \Controller {
protected $note;
public function __construct(Note $note)
{
$this->note = $note;
}
public function getNote($url_id, $key)
{
$note = $this->note->find($url_id, $key);
if (!$note) {
return \Response::json(array('message' => 'Note not found'), 404);
}
$this->note->delete($note->id);
return \Response::json($note);
}
...
I've injected my Note interface in to my controller and all is well.
My Test
use \Mockery as M;
class OneTimeNoteTest extends TestCase {
public function setUp()
{
parent::setUp();
$this->mock = $this->mock('OneTimeNote\Repositories\EloquentNoteRepository');
}
public function mock($class)
{
$mock = M::mock($class);
$this->app->instance($class, $mock);
return $mock;
}
public function testShouldReturnNoteObj()
{
// Should Return Note
$this->mock->shouldReceive('find')->once()->andReturn('test');
$note = $this->call('GET', '/note/1234567890abcdefg/1234567890abcdefg');
$this->assertEquals('test', $note->getContent());
}
}
...
The error I'm getting
1) OneTimeNoteTest::testShouldReturnNoteObj
ErrorException: Trying to get property of non-object
/Users/andrew/laravel/app/OneTimeNote/Controllers/NoteController.php:24
Line 24 is in reference to this line found in my controller:
$this->note->delete($note->id);
Basically my abstracted repository method delete() obviously can't find $note->id because it really doesn't exist in the testing environment. Should I create a Note within the test and try to actually deleting it? Or would that be something that should be a model test? As you can see I need help, thanks!
----- Update -----
I tried to stub the repository to return a Note object as Dave Marshall mentioned in his answer, however I'm now receiving another error.
1) OneTimeNoteTest::testShouldReturnNoteObj
BadMethodCallException: Method Mockery_0_OneTimeNote_Repositories_EloquentNoteRepository::delete() does not exist on this mock object
I do have a delete() method in my repository and I know it's working when I test my route in the browser.
public function delete($id)
{
Note::find($id)->delete();
}
You are stubbing the note repository to return a string, PHP is then trying to retrieve the id attribute of a string, hence the error.
You should stub the repository to return a Note object, something like:
$this->mock->shouldReceive('find')->once()->andReturn(new Note());
Building upon Dave's answer, I was able to figure out what my problem is. I wasn't mocking the delete() method. I didn't understand the need to mock each individual method in my controller that would be called.
I just added this line:
$mock->shouldReceive('delete')->once()->andReturnNull();
Since my delete method is just deleting the note after it is found, I went ahead and mocked it but set it to return null.

Recomputing entity changeset in onFlush listener

Consider the following schema:
[Work]
id
tags ManyToMany(targetEntity="Tag", inversedBy="works", cascade={"persist"})
[Tag]
id
works_count
works ManyToMany(targetEntity="Work", mappedBy="tags")
works_count is a counter cache for Tag::works.
I have a onFlush listener on Work that checks if Work::tags has changed, and updates each of the tags' works_count.
public function onFlush(OnFlushEventArgs $args)
{
foreach ($uow->getScheduledEntityUpdates() as $work) {
$changedTags = /* update relevant tags and return all the changed ones */
$metadata = $em->getClassMetadata('Acme\Entity\Tag');
foreach ($changedTags as $tag) {
$uow->recomputeSingleEntityChangeSet($metadata, $tag);
}
}
}
Now if I read the changesets of the updated tags, the changes of works_count appears correctly, but they don't get updated in the database..
If I replace recomputeSingleEntityChangeSet() with computeChangeSet() then everything works as expected and the DB is updated, but computeChangeSet() has an #internal Don't call from the outside. annotation on it, so I'm not sure what the consequences are..
Every source on the internet says to use recomputeSingleEntityChangeSet so why doesn't it work in this case?
P.S
The tags are managed by the EntityManager ($em->contains($tag) returns true)
This problem was related with a bug in UnitOfWork and finally it's fixed with the release of Doctrine ORM 2.4.3 on September 11, 2014. See DDC-2996 for details.
It seems that Doctrine 2.2 can merge change sets or generate new change sets, but it needs to know which. If you get it wrong, it will either replace your existing change sets or do nothing at all. I'd be very interested to know if there is a better option than this, or if this is even right.
if($uow->getEntityChangeSet($entity)) {
/** If the entity has pending changes, we need to recompute/merge. */
$uow->recomputeSingleEntityChangeSet($meta, $contact);
} else {
/** If there are no changes, we compute from scratch? */
$uow->computeChangeSet($meta, $entity);
}
In doctrine 2.4.1, use recomputeSingleEntityChangeSet only if you are changing tag in the event listener AND UOW contain tag ChangeSet (Change that happen outside of the event listener). Basically recomputeSingleEntityChangeSet is a function to merge ChangeSet for an entity.
Doc from the function
The passed entity must be a managed entity. If the entity already has a change set because this method is invoked during a commit cycle then the change sets are added whereby changes detected in this method prevail.
NOTE: You need to make sure UOW already have ChangeSet for the entity, otherwise it will not merge.
For future readers, at all cost try to avoid the listeners. Those are hardly testable, your domain should not rely on magic. Consider OP's test case how to achieve the same without Doctrine events:
Work class:
public function addTag(Tag $tag): void
{
if (!$this->tags->contains($tag)) {
$this->tags->add($tag);
$tag->addWork($this);
}
}
Tag class:
public function addWork(Work $work): void
{
if (!$this->works->contains($work)) {
$work->addTag($this);
$this->works->add($work);
$this->worksCount = count($this->works);
}
}
TagTest class:
public function testItUpdatesWorksCountWhenWorkIsAdded()
{
$tag = new Tag();
$tag->addWork(new Work());
$tag->addWork(new Work());
$this->assertSame(2, $tag->getWorkCount());
}
public function testItDoesNotUpdateWorksCountIfWorkIsAlreadyInCollection()
{
$tag = new Tag();
$work = new Work();
$tag->addWork($work);
$tag->addWork($work);
$this->assertSame(1, $tag->getWorkCount());
}

How should controller actions be tested by redirecting in Zend Framework 2?

How can controller actions like these:
public function redirectFromAction() {
// route "my-route" is mapped to the action redirectToAction
return $this->redirect()->toRoute('my-route');
}
public function redirectToAction() {
return new ViewModel();
}
be tested with PHPUnit? What can and should be tested? Which asserts? What is special about the testing of controller actions with request redirecting?
It would be nice, if someone could provide an example for the testing of the actions above.
This doc could help you: http://framework.zend.com/manual/2.1/en/tutorials/unittesting.html
I'd test something like this:
public function testRedirectAction()
{
$this->dispatch('/redirect-form');
$this->assertResponseStatusCode(301);
$this->assertResponseHeaderContains('Location' , '/my-route');
}
Be sure that you extend you test class from AbstractHttpControllerTestCase