Symfony 6.2 MapEntity to use custom finder? - php

I have two databases, MYSQL and MongoDB, as MongoDB works with Documents I need a way to fetch data from MongoDB using in-URL parameters. Sensio/Framework-Extra-Bundle had this feature, but its going to be abandoned and I decided to switch on Symfony Attributes instead.
With Sensio/Framework-Extra-Bundle #[ParamConverter] that would look something like this:
#[Route('/{slug}', methods: ['GET'])]
#[ParamConverter('slug', options: ['finder' => 'CustomFinder', 'attributes' => ['id']])]
Since they switched over to Symfony Attributes and now they are using #[MapEntity] I'm not sure this is possible anymore.
I have tried to do similar with #[MapEntity] like this:
#[MapEntity(mapping: ['finder' => 'CustomFinder', 'slug' => 'id'])] MyDocument $document
But I'm getting this error:
Uncaught Error: App\Controller\MyController::get(): Argument #1 ($document) must be of type App\Document\MyDocument, string given, called in /app/vendor/symfony/http-kernel/HttpKernel.php on line 163

Try this:
#[MapEntity(mapping: ['finder' => 'CustomFinder', 'id' => 'slug'])] MyDocument $document

Related

Laravel dispatch plain json on queue

I have 2 simple questions overall. Im currently looking into some event handling in Laravel and would like to use RabbitMQ as my event store. Therefor i installed this package to start with: https://github.com/php-enqueue/enqueue-dev
To get started i registered it and i am able to push messages on to RabbitMQ:
$job = (new Sendemail())->onQueue('email')->onConnection('interop');
dispatch($job);
The problem however is that Laravel pushes a certain format on the queue and i can't figure out how to change that. An example message would be:
{
"job":"Illuminate\\\\Queue\\\\CallQueuedHandler#call",
"data":{
"command":"O:29:\\"Acme\\Jobs\\FooJob\\":4:{s:11:\\"fooBar\\";s:7:\\"abc-123\\";s:5:\\"queue\\";N;s:5:\\"delay\\";N;s:6:\\"\\u0000*\\u0000job\\";N;}"
}
}
So the question is, how can i change this? The main reason on this is that the consumer side is not even a PHP application which also can not interpret the PHP serialized model. Therefor im looking for a way to push a plain JSON object instead.
From the other hand i would also like to understand how you could build a custom listener? For the listener the same thing happens. Laravel tries to read the method but when i push plain JSON this will never work. Isn't there a way to register a handler on a topic and do further handling of the payload of the message within the handler itself?
There is a simple way for your purpose:
First install this package for rabbit:
vladimir-yuldashev/laravel-queue-rabbitmq
and in controller:
Queue::connection('rabbitmq')->pushRaw('{you can generate a json format here}', 'queue_name');
you can generate a json and put in this command.
There's a laravel-queue library that works with the php-enqueue library you linked to make it compatible with Laravel's built in queue system that Florian mentioned.
By default, it will still use a serialized object, but I think that can be overridden. If you look in Queue.php, createObjectPayload() on line 130 in the core Laravel Framework, that's where the job is being serialized.
If you extend the Queue class in the laravel-queue library, you should be able to change createObjectPayload to look something like this:
protected function createObjectPayload($job, $queue)
{
$payload = $this->withCreatePayloadHooks($queue, [
'displayName' => $this->getDisplayName($job),
'job' => 'Illuminate\Queue\CallQueuedHandler#call',
'maxTries' => $job->tries ?? null,
'timeout' => $job->timeout ?? null,
'timeoutAt' => $this->getJobExpiration($job),
'data' => [
'commandName' => $job,
'command' => $job,
],
]);
return array_merge($payload, [
'data' => [
'commandName' => get_class($job),
'command' => json_encode(clone $job),
],
]);
}
That should JSON encode the job data instead of serializing it. You may even be able to remove the encoding altogether, as I think it's already JSON encoded somewhere up the chain.

How can I add handler to database query results in Lithium

I am using the Lithium framework version 1.1.1 in PHP 5.6 with MongoDB. I have updated MongoDB from 3.4 to 3.6 and this ended up requiring the PHP ini variable mongo.long_as_object be set to true for the aggregateCursor() methods to work properly in the legacy MongoDB driver for PHP. This version of Lithium does not yet support the newer MongoDB PHP module. This causes a problem with the way NumberLong values are handled in Lithium since they are converted to a MongoInt64 in PHP.
For example: When calling $results->data() on a DocumentSet, a BSON result such as { viewers: NumberLong(12345) } will decode to [ 'viewers' => [ 'value' => '12345' ] ]. Instead I need the PHP array to be [ 'viewers' => 12345 ].
If I add an appropriate handler directly in the lithium\data\entity\Document::_init method then everything works as I expect. For example:
$this->_handlers += [
'MongoId' => function($value) { return (string) $value; },
'MongoDate' => function($value) { return $value->sec; },
'MongoInt64' => function($value) { return (int) $value->value; }
];
However, directly editing the Lithium library is likely not the best approach especially when upgrading the library to newer version as they are released. Is there a proper way to add this handler elsewhere? Such as in the Connections::add(...) method in the connections.php bootstrap file?
Unfortunately, handlers aren't directly configurable, however, they're not too hard to override. You can pass a classes key to Connections:add(), which allows you to extend one of the two classes where handlers are specified, i.e.:
Connections::add([
/* ... */,
'classes' => [
'entity' => 'my\data\Document'
// -- or --
'schema' => 'my\data\Schema'
]
]);
From there, you can implement your custom class that extends the appropriate core class, adding extra handlers as appropriate. Also, a PR to add MongoInt64 support to Li3 core would be gratefully accepted. :-)

MapReduce with PHP and MongoDB

I'm fairly new to Mongo and I have what I thought was a simple question. How do I do MapReduce with PHP and the non legacy MongoDB driver http://php.net/manual/en/set.mongodb.php or the higher level package mongodb/mongodb found at https://packagist.org/packages/mongodb/mongodb?
Every example I've seen seems to use the legacy driver (http://php.net/manual/en/book.mongo.php). They all use the MongoCode object, which doesn't exist in mongodb.php. It exists in mongo.php (the legacy driver). When I try and use it, it will say that "Class 'MongoCode' not found".
My code looks something like:
$function = "function() { emit(this); }";
$map = new \MongoCode($function);
$command = $db->command([
"mapreduce" => "db.archiveData",
"map" => $map,
"query" => $query,
"out" => "data"
]);
To make things more confusing, when I look at the source at https://github.com/mongodb/mongo-php-library, there is a unit test for MapReduce (https://github.com/mongodb/mongo-php-library/blob/4dc36f6231df133a57ff0dc5a0123945133d25ba/tests/Operation/MapReduceFunctionalTest.php). But it uses the MongoDB\Operation\MapReduce, which doesn't seem to exist in the 1.1 version of mongodb/mongodb.
I thought maybe I would call it on the server using JavaScript. But when I look at http://php.net/manual/en/mongodb.execute.php, it says it "is deprecated in MongoDB 3.0+". So that doesn't feel like something I should use.
So is it that:
MapReduce is not supported with mongodb/mongodb. Or maybe it is not supported yet, but will be?
I have to use the legacy driver for MapReduce?
I have to figure out a way to call db.collection.mapReduce via JavaScript on the server?
I have to use the Aggregation Pipeline (https://docs.mongodb.com/manual/aggregation/) to do map reduce type of actions? But that feels much more limited.
What am I missing?
So I now have clarity on where things are at.
MapReduce will be officially supported in 1.2.0 of PHPLib (https://jira.mongodb.org/browse/PHPLIB-53)
Until then, there is a completely usable workaround by using the command object as per https://docs.mongodb.com/php-library/current/upgrade/#mapreduce-command-helper
Example is here as well:
$database = (new MongoDB\Client)->selectDatabase('db_name');
$cursor = $database->command([
'mapReduce' => 'collection_name',
'map' => new MongoDB\BSON\Javascript('...'),
'reduce' => new MongoDB\BSON\Javascript('...'),
'out' => 'output_collection_name',
]);
$resultDocument = $cursor->toArray()[0];
You can also use MapReduce via Doctrine (http://docs.doctrine-project.org/projects/doctrine-mongodb-odm/en/latest/reference/map-reduce.html), but that is using legacy and a shim. So probably not a good choice for a new project.

Multiple Controllers Zend Framework 2 - Entity Manager Doctrine 2

I am using Zend Framework 2.2.2 and Doctrine2 Module 0.7.0.
My goal is to have my functions related to a task in a standalone php-class. My current workflow is between two different programms: get data -> modify and store data -> send data.
This workflow needs functions from 3 ZF2 modules:
1. source software module
2. internal storage mechanism module
3. destination software module
The first task is successfull but when I move my data to the second module like this (shrinked to the main code):
use MTNewsletterEngine\Controller\NewsletterEngineController;
/** #var \MTNewsletterEngine\Controller\NewsletterEngineController */
private $_newsletterEngine;
$this->_newsletterEngine = new NewsletterEngineController();
[...]
$this->_newsletterEngine->addNewNewsletterRecipient($emailAddresses,1);
The second Controller has problems getting the service locator:
Fatal error: Call to a member function get() on a non-object in C:\xampp\htdocs\app\trunk\module\MTNewsletterEngine\src\MTNewsletterEngine\Controller\NewsletterEngineController.php on line 51
Line 51:
$em_mtnewsletterengine = $this->getServiceLocator()->get('doctrine.entitymanager.orm_mtnewsletterengine');
NewsletterEngineController is the Main Controller from Module MTNewsletterEngine.
I am confused as I don't know how to get this solved. Thanks.
Do not create a new instance of NewsletterEngineController by using the new keyword. The ServiceLocator will not be injected to the created object this way. Use Zend\ServiceManager to retrieve an instance of Zend\Mvc\Controller\ControllerManager (alias: "ControllerLoader" (ci)) and use the get method, to load the target controller. Zend\Mvc\Controller\ControllerManager extends the ServiceManager itself (because it is a plugin manager).
Check your module.config.php. The controller should be listed as an invokable controller.
Example:
'controllers' => array(
'invokables' => array(
'MTNewsletterEngine\Controller\NewsletterEngine' => 'MTNewsletterEngine\Controller\NewsletterEngineController'
),
),
$this->_newsletterEngine = $this->getServiceLocator()
->get('ControllerLoader')
->get('MTNewsletterEngine\Controller\NewsletterEngine');
For more information read the manual and try to understand the way the ServiceManager / ServiceLocator (which is part of Zend\Di) works.
Maybe you should also think about the structure of your application. I am not sure what you are trying to do there but it seems like you are mixing up different application layers.
Docs
http://framework.zend.com/manual/2.2/en/index.html#zend-di
http://framework.zend.com/manual/2.2/en/index.html#zend-servicemanager

Cannot generate zf2 DoctrineORMModule entities from mapping files

I have zf2 DoctrineORMModule and DoctrienModule installed. I am trying to use the command tool to create mapping files and generate entities from these mapping files. (I know this isn't the preferred method, but this is how I'm going to do it. I have my reasons.)
I have a restful module configured and here is my Doctrine Configuration for this module.
// Doctrine config
'doctrine' => array(
'driver' => array(
'Restful_driver' => array(
'class' => 'Doctrine\ORM\Mapping\Driver\AnnotationDriver',
'cache' => 'array',
'paths' => array(__DIR__ . '/../src/Restful/Entities')
),
'orm_default' => array(
'drivers' => array(
'Restful\Entities' => 'Restful_driver'
)
)
)
)
I first run
doctrine orm:convert-mapping xml /to/my/dest/path --from-database --force
This will create my xml file with all the table info. This part works fine and I can view the xml that it created. Next I try to run
doctrine orm:generate-entities /to/my/dest/path --generate-annotations
--generate-methods
I don't get any errors but also I don't get any results either. The output from the previous command is.
No Metadata Classes to process.
I have tried to read around but havn't found any articles that really solve my problem. Most say something about not having my annotations/mappings not configured correctly. But I can dump the entity manager through a controller.
var_dump($this->getServiceLocator()->get('doctrine.entitymanager.orm_default'));
What do I need to do to get this to generate entities from xml mappings? Any help is appreciated.
I had a similar problem with YAML files and posted my solution here. I'm sure this will work with xml files as well. Just try to add
$driverImpl = new \Doctrine\ORM\Mapping\Driver\XmlDriver(array("YOUR_PATH_TO_XML_FILES"));
/* #var $em \Doctrine\ORM\EntityManager */
$em = $application->getServiceManager()->get('doctrine.entitymanager.orm_default');
$em->getConfiguration()->setMetadataDriverImpl($driverImpl);
to the doctrine-module.php.
Did you try using the doctrine-module script in vendor/bin? It should be all set up already to read your app's configs.
./doctrine-module orm:generate-entities ~/doctrine-entities
This "error" occured because you use Annotation Driver for generating. This driver uses your current exist entities and doesnt look at xml. If you want to create Entities from XML - you should send to DI in doctrine configuration section XML driver with need path.
I use another zf2 doctrine module and my DI config has another format, so i cant to send you properly DI example.

Categories