Detach entity at doctrine pre* events - events

Is it possible to detach entity at (for example) prePersist event?
public function prePersist(LifecycleEventArgs $args)
{
$entity = $args->getEntity();
$em = $args->getEntityManager();
$em->detach($entity);
}
In this way entity will not be detached. prePersist function is in the service:
abc.saver:
class: App\AbcBundle\Layer\Saver
arguments: ["#doctrine.orm.entity_manager"]
tags:
- { name: doctrine.event_listener, event: prePersist }
P.S. I resolved this using onFlush event:
foreach ($uow->getScheduledEntityInsertions() as $entity)
{
$em->detach($entity);
}
but I am interesting how can I reach this using doctrine pre* behaviors (Lifecycle Events)?

Related

Laravel OBSERVER update process

In SESSObServer
public function updated(SESS $sESS)
{
log::info("data updated");
if ($sESS->wasChanged('is_active')) {
log::info("data updated");
}
}
IN Observer, created(SESS $sESS) function run but update is not run. How can i solve problem ?
I changed my query. Now, this is run.
I didn't use get. I used first()
$sess = SESS::where('sess_id', request('id'))->first();
$sess->save();
Make sure your Observer is registered.
# app/Providers/EventServiceProvider.php
use App\Models\SESS;
use App\Observers\SESSObserver;
class EventServiceProvider extends ServiceProvider
{
public function boot()
{
SESS::observe(SESSObServer::class);
}
}
Make sure you're using the correct classes. The logger's class is Log (capital L)
public function updated(SESS $sESS)
{
Log::info("data updated");
if ($sESS->wasChanged('is_active')) {
Log::info("data updated");
}
}
Alternatively, I think you can still use info().
public function updated(SESS $sESS)
{
info("data updated");
if ($sESS->wasChanged('is_active')) {
info("data updated");
}
}
If you're using apache, make sure the apache user can write to the log file.

How to set listener queue name from environment variable?

I just noticed that some of my listeners do not use the queue I expected them to use. Our team upgraded from Laravel 5.2 to 5.5 a few weeks back, and I guess this is when the problem started happening. There hasn't been much load on the system, so I only discovered it by accident.
Anyway. I used to set the queue name on the listener through a queue method, like so:
public function queue(QueueManager $handler, $method, $arguments): void
{
$handler->connection()->push($method, $arguments, Queue::getNotificationQueue());
}
This approach is not working anymore, so a default queue ends up handling the job instead of the expected notification queue.
So I looked at the documentation https://laravel.com/docs/5.5/events#queued-event-listeners, which states that the name should be set on a queue property on the listener. My problem is that I have the queue name in an environment variable, so I cannot just set it directly as a property, as shown in the documentation and it does not work to set it on the constructor, like so:
protected $queue;
public function __construct()
{
$this->queue = Queue::getNotificationQueue();
}
Does anyone here have an idea of how I can get around this?
Specifically for SQS queues the $queue property acts a bit weird because it doesn't seem to refer to queues defined in queue.php, but it expects a full queue url, so even the example in the documentation seems off.
But for dynamic queue names on queued event listeners that for example changes depending on environment, making a custom SqsConnector and SqsQueue will be one way to solve your issue.
Here is an example of implementation.
ACMEEventListener.php
class ACMEEventListener implements ShouldQueue
{
public function handle(Event $event): void
{
// I'm going to a custom queue
}
public static function getQueue(): string
{
return 'https://sqs.eu-central-1.amazonaws.com/<account id>/<queue name>';
}
}
CustomSqsConnector.php
use Illuminate\Queue\Connectors\SqsConnector;
use Aws\Sqs\SqsClient;
class CustomSqsConnector extends SqsConnector
{
public function connect(array $config)
{
$sqs = new SqsClient($config);
return new CustomSqsQueue($sqs, $config['queue']);
}
}
CustomSqsQueue.php
class CustomSqsQueue extends \Illuminate\Queue\SqsQueue
{
public function push($job, $data = '', $queue = null)
{
if ($job instanceof CallQueuedListener && method_exists($job->class, 'getQueue')) {
$queue = $job->class::getQueue();
}
return $this->pushRaw($this->createPayload($job, $data), $queue);
}
}
CustomSqsQueueServiceProvider.php
class CustomSqsQueueServiceProvider extends ServiceProvider
{
public function register(): void
{
$this->app->booted(function () {
$this->app['queue']->extend('custom_sqs', function () {
return new CustomSqsConnector;
});
});
}
}
And then in your queue.php, your default SQS connection driver from sqs to custom_sqs

Angular Material 2: How to refresh md-table after editing a record?

So I have a user-list component and a user-detail component.
The user-list component has an md-table listing all users registered. The user is able to click on a button to see the details of the corresponding entity.
After editing the Name property and saving (for example), the user-detail redirects to the user-list component. But the md-table displays the old information.
How can I trigger an md-table refresh after editing the entity in another component like this scenario?
Here is my user-list component:
export class UserListComponent implements OnInit {
tableDisplayedColumns: string[] = ['userName', 'email', 'options'];
userDataSource: UserDataSource;
constructor(private _userService: UserService, private _router: Router) { }
ngOnInit(): void {
this.userDataSource = new UserDataSource(this._userService);
}}
class UserDataSource extends DataSource<UserVModel> {
constructor(private _userService: UserService) {
super();
}
connect(): Observable<UserVModel[]> {
return this._userService.getAllUsers();
}
disconnect(): void { }}
The table will re-render when the stream provided by connect() emits a new value.
getAllUsers needs to emit a new set of data when it is changed. Otherwise, listen for a separate stream (e.g. dataChanged) from the _userService and use that to call getAllUsers again.
connect(): Observable<UserVModel[]> {
return this._userService.dataChanged
.switchMap(() => this._userService.getAllUsers()
}
Actually the problem was that the User-Detail component was redirecting before the observable had the chance to complete. So I placed the router.navigate as a complete function.
Code Before
onSubmit() {
this._updateSub = this._service.updateUser(this._id, this._userForm.value)
.subscribe(null, err => this.onSubmitError(err));
this._router.navigate(['/user']);
}
Code After
onSubmit() {
this._updateSub = this._service.updateUser(this._id, this._userForm.value)
.subscribe(null, err => this.onSubmitError(err), () => this.afterSubmit());
}
afterSubmit() {
this._router.navigate(['/user']);
}
Prior to this change, I was getting the old values after the redirect. Using the complete function, I get up to date values without the need to use a dataChanged observable in the service.
You could tidy things up and put your router navigation inside the response :
onSubmit() {
this._updateSub = this._service.updateUser(this._id,
this._userForm.value).subscribe(
res => this._router.navigate(['/user']);
err => this.onSubmitError(err), () => this.afterSubmit());
}

Life cycle callback prePersist not firing (using YAML files)

I'm learning Symfony following The Book. In the tutorial, I successfully configured prePersist events (to set the createdAt field at insert time).
Now I'm trying to do the same but with YAML files instead of annotations. Here my orm.yml file:
AppBundle\Entity\Chronicle:
type: entity
table: chronicles
id:
id:
type: integer
generator: {strategy: AUTO}
fields:
name:
type: string
length: 256
createdAt:
type: datetime
manyToOne:
creator:
targetEntity: User
inversedBy: chronicles
joinColumn:
name: user_id
referencedColumnName: id
game:
targetEntity: Game
joinColumn:
name: game_id
referencedColumnName: id
oneToMany:
characters:
targetEntity: Character
mappedBy: chronicle
lifeCycleCallbacks:
prePersist: [ setCreatedAtValue ]
And this is a snippet of my entity class:
class Chronicle
{
private $id;
private $name;
private $createdAt;
// Associations
private $game;
private $creator;
private $characters;
// TODO: users or user relationships
// Constructor
public function __construct(User $creator=null) {
$this->characters = new ArrayCollection();
$this->creator = $creator;
}
/**
* Set createdAt at the current time.
*
* (Lifecycle callback)
*/
public function setCreatedAtValue()
{
$this->createdAt = new \DateTime();
}
// ...
}
But the setCreatedAtValue() method is never called. So, I get an exception when I try to insert an object.
I have noticed that when using annotations I have to tell about the existence of lifecycle callbacks with the #ORM\HasLifecycleCallbacks() annotation, but I have not found anywhere an equivalent to that in yml, or if that is needed.
In a tutorial I have found I should register the callback in services.yml, but the tutorial never mention it, and I haven't found it anywhere else.
It will be called if you change "lifeCycleCallbacks" to "lifecycleCallbacks".

Testing Laravel Event Listener

I'm trying to test Event Listener in Laravel.
This is my listener:
class FlashNotifier
{
public function handleMenuItemWasStored()
{
Session::flash('flash-status', 'success');
Session::flash('flash-message', 'Item was stored.');
}
public function subscribe($events)
{
$events->listen(
MenuItemWasStored::class,
'\App\Listeners\Menu\FlashNotifier#handleMenuItemWasStored'
);
}
}
And this is the test I came up so far:
public function testEventListenerWasTriggered()
{
$listener = Mockery::mock('App\Listeners\Menu\FlashNotifier');
$d = new Dispatcher;
$listener->shouldReceive('handleMenuItemWasStored')->once();
$d->fire('App\Events\Menu\MenuItemWasStored');
}
However, I get the following exception:
1) FlashListenerTest::testEventListenerWasTriggered
Mockery\Exception\InvalidCountException: Method handleMenuItemWasStored() from Mockery_1_App_Listeners_Menu_FlashNotifier should be called
exactly 1 times but called 0 times.
What am I doing wrong?

Resources