FosElasticaBundle: define mapping for GUID / UUID fields - symfony

How do I define the mappings in config_yml if my entities uses GUID field type as identifier?
Details
I have a project with Symfony 2.7.3 and FosElasticaBundle 3.1.4.
I have configured the mapping and everything works fine when I use the populate command or if I edit an existing entity; but when creating a new entity I get an exception
"expected a simple value for field [_id] but found [START_OBJECT]]"
Checking the JSON response I have noticed that _id is set to an empty object ..."_id":{}....
The Uuid class already has a __toString method which I expect should do the trick, but I am probably missing something.
Current mappings
fos_elastica:
clients:
default: { host: %elasticsearch_host%, port: %elasticsearch_port%, logger: true }
indexes:
app:
types:
user:
mappings:
name: ~
surname: ~
persistence:
driver: orm
model: AppBundle\Entity\User
provider: ~
listener: ~
finder: ~
Thank you.

There are no specific mappings for UUID/GUID fields.
The identifier is retrieved using the standard Symfony property accessor, so the solution to my problem was to change the getId() method in my entity, and make it cast to string be
public function getId()
{
return (string)$this->id;
}

Related

Query on Doctrine class table inheritance with required fields

My domain has a parent IncidenceMessage class and several child classes (i.e. IncidenceMessageText).
I have the following class table inheritance configuration:
Domain\Model\IncidenceMessage\IncidenceMessage:
type: entity
repositoryClass: Infrastructure\Domain\Model\IncidenceMessage\DoctrineIncidenceMessageRepository
table: incidence_messages
inheritanceType: JOINED
discriminatorColumn:
name: type
type: string
length: 30
discriminatorMap:
text: IncidenceMessageText
image: IncidenceMessageImage
audio: IncidenceMessageAudio
video: IncidenceMessageVideo
fields:
...
I can create any IncidenceMessage entity correctly.
Having only a IncidenceMessageText in database, when I try to fetch incidence messages I get the following error:
TypeError: Argument 1 passed to Domain\Model\File\FileId::__construct() must be of the type string, null given
(FileId is a value object that represents the id of a File entity)
IncidenceMessageImage has a File field that is a foreign key and it is required.
It makes no sense to me that Doctrine fetches File when IncidenceMessageText doesn't have that field.
While debugging, I discovered that doctrine does a SELECT with LEFT JOINs with every single IncidenceMessage table and this calls my FileTypeId::convertToPHPValue method:
class FileIdType extends Type
{
public function convertToDatabaseValue($value, AbstractPlatform $platform)
{
return new FileId($value);
}
}
AFAIK, the problem here is that child classes have required fields but that shouldn't be a stopper, right?
I found a possible workaround. On my custom DBAL Type FileIdType, I checked if the value was null before instantiating FileId:
use Doctrine\DBAL\Types\Type;
class FileIdType extends Type
{
public function convertToPHPValue($value, AbstractPlatform $platform)
{
return $value ? new FileId($value) : null;
}
}

Service "fos_elastica.finder.app.user" not found

Symfony can't find service for fos_elastica
Service "fos_elastica.finder.app.user" not found: even though it exists in the app's container, the container inside "App\Controller\DevController" is a smaller service locator that only knows about the "doctrine", "form.factory", "http_kernel", "parameter_bag", "request_stack", "router", "security.authorization_checker", "security.csrf.token_manager", "security.token_storage", "session" and "twig" services. Unless you need extra laziness, try using dependency injection instead. Otherwise, you need to declare it using "DevController::getSubscribedServices()".
my config
# Read the documentation: https://github.com/FriendsOfSymfony/FOSElasticaBundle/blob/master/Resources/doc/setup.md
fos_elastica:
clients:
default: { host: localhost, port: 9200 }
# indexes:
# app: ~
indexes:
app:
client: default
types:
user:
properties:
username: ~
# mappings:
# email: ~
persistence:
# the driver can be orm, mongodb, phpcr or propel
# listener and finder are not supported by
# propel and should be removed
driver: orm
model: App\Entity\User
provider: ~
listener: ~
finder: ~
my controller:
/**
* #Route("/search")
*/
public function searchElastic(){
/** var array of App\Entity\User */
$finder = $this->container->get('fos_elastica.finder.app.user');
return new response('N/A');
}
command php bin/console fos:elastica:populate didn't throw any errors and in phpstorm it isn't highlighted (it mean phpstorm found it)
Please help me.
You should use Dependency Injection like so:
Add use statement in the controller class use FOS\ElasticaBundle\Manager\RepositoryManagerInterface;, then your action should look like this:
/**
* #Route("/search")
*/
public function searchElastic(RepositoryManagerInterface $finder) {
$someResult = $finder->getRepository(User::class)->find(...);
return new response('N/A');
}

Example on how to config google cloud storage with KnpGaufetteBundle

I am trying to configure a KnpGaufretteBundle to use Google Cloud Storage for storing my files. This is the config:
## definition of the GCS service
app.google_cloud_storage.service:
class: \Google_Service_Storage
factory_class: Knp\Bundle\GaufretteBundle\DependencyInjection\Factory\GoogleCloudStorageAdapterFactory
factory_method: 'create'
arguments:
- "123#developer.gserviceaccount.com"
- "http://localhost/file.p12"
- "pwd"
## config of knp_gaufrette
knp_gaufrette:
stream_wrapper: ~
adapters:
gcs_minn_images:
google_cloud_storage:
service_id: 'app.google_cloud_storage.service'
bucket_name: 'minn-images'
filesystems:
gcs_minn_images_fs:
adapter: gcs_minn_images
The error message I got is:
ContextErrorException in GoogleCloudStorageAdapterFactory.php line 16:
Catchable Fatal Error: Argument 1 passed to Knp\Bundle\GaufretteBundle\DependencyInjection\Factory\GoogleCloudStorageAdapterFactory::create() must be an instance of Symfony\Component\DependencyInjection\ContainerBuilder, string given, called in /home/amine/NetBeansProjects/tuto/app/cache/dev/appDevDebugProjectContainer.php on line 724 and defined
According to the error message, I gave a string of stead of ContainerBuilder. Great! Let's add the ContainerBuilder to the arguments as follows:
## definition of the GCS service
app.google_cloud_storage.service:
class: \Google_Service_Storage
factory_class: Knp\Bundle\GaufretteBundle\DependencyInjection\Factory\GoogleCloudStorageAdapterFactory
factory_method: 'create'
arguments:
- #service_container
- "123#developer.gserviceaccount.com"
- "http://localhost/file.p12"
- "pwd"
The result is again an error:
Catchable Fatal Error: Argument 1 passed to Knp\Bundle\GaufretteBundle\DependencyInjection\Factory\GoogleCloudStorageAdapterFactory::create() must be an instance of Symfony\Component\DependencyInjection\ContainerBuilder, instance of appDevDebugProjectContainer given, called in /home/amine/NetBeansProjects/tuto/app/cache/dev/appDevDebugProjectContainer.php on line 724 and defined
So now, the error is telling me that I provide an instance of appDevDebugProjectContainer in stead of ContainerBuilder!!
Ok, let's have a look to /home/amine/NetBeansProjects/tuto/app/cache/dev/appDevDebugProjectContainer.php on line 724...
class appDevDebugProjectContainer extends Container{
// ...
/**
* Gets the 'app.google_cloud_storage.service' service.
*
* This service is shared.
* This method always returns the same instance of the service.
*
* #return \Google_Service_Storage A Google_Service_Storage instance.
*/
protected function getApp_GoogleCloudStorage_ServiceService()
{
return $this->services['app.google_cloud_storage.service'] =\Knp\Bundle\GaufretteBundle\DependencyInjection\Factory\GoogleCloudStorageAdapterFactory::create($this, '123#developer.gserviceaccount.com', 'http://localhost/file.p12', 'pwd');
}
I am really lost...
So, is there any complete example to config google cloud storage?
I finally found the solution. You have to create your own factory class as described in the documentation of the bundle:
Factory class
<?php
namespace Minn\AdsBundle\Factory;
/**
* Description of GoogleCloudStorageServiceFactory
*/
class GoogleCloudStorageServiceFactory {
public function createService() {
// creating the google client
$client = new \Google_Client();
// setting the service acount credentials
$serviceAccountName = '123#developer.gserviceaccount.com';
$scopes = array(
'https://www.googleapis.com/auth/devstorage.read_write',
);
$privateKey = file_get_contents('http://localhost/f.p12');
$privateKeyPassword = 'pwd';
$credential = new \Google_Auth_AssertionCredentials(
$serviceAccountName, $scopes, $privateKey, $privateKeyPassword);
// set assertion credentials
$client->setAssertionCredentials($credential);
// creating and returning the service
return new \Google_Service_Storage($client);
}
}
The config.yml file
app.google_cloud_storage.service:
class: \Google_Service_Storage
factory: [Minn\AdsBundle\Factory\GoogleCloudStorageServiceFactory, createService]
knp_gaufrette:
stream_wrapper: ~
adapters:
gcs_images:
google_cloud_storage:
service_id: 'app.google_cloud_storage.service'
bucket_name: 'images'
filesystems:
gcs_images_fs:
adapter: gcs_images
vich_uploader:
db_driver: orm
storage: gaufrette
mappings:
motors_files:
upload_destination: gcs_images_fs
namer: vich_uploader.namer_origname
delete_on_remove: true
That's was it...
Hope it will help others...

how to pass username to rollbar via monolog, symfony2

I am using rollbar.com to collect all details about exceptions in symfony2 app. However I don't understand how can I configure monolog so it would pass username and user id to rollbar.
I see that I can pass rollbar config as shown here and I am thinking person_fn is what I need. Still I don't know where to put this function (this should be in service because I need to check security token) and how to pass it to rollbar.
# config_prod.yml
rollbar:
type: rollbar
level: error
token: %rollbar_token%
config:
person_fn: getUserForRollbarRightAboutNowOrSomething
Found solution:
update monolog/monolog bundle to at least 1.17.0 version.
create ContextProcessor and update user information
#src/AppBundle/Monolog/RollbarContextProcessor
namespace AppBundle\Monolog;
use AppBundle\Entity\User;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
class RollbarContextProcessor
{
private $tokenStorage;
public function __construct(TokenStorageInterface $tokenStorage)
{
$this->tokenStorage = $tokenStorage;
}
public function processRecord($record)
{
if ($this->tokenStorage->getToken()) {
$user = $this->tokenStorage->getToken()->getUser();
if ($user instanceof User) {
$record['context']['payload']['person'] = [
'id' => $user->getId(),
'username' => $user->getUsername(),
'email' => $user->getEmail(),
];
}
}
return $record;
}
}
configure ContextProcessor as service with monolog.processor tag.
# app/config/config_prod.yml
services:
monolog.processor.rollbar_context:
class: AppBundle\Monolog\RollbarContextProcessor
arguments: [#security.token_storage]
tags:
- { name: monolog.processor, method: processRecord, handler: rollbar }
monolog:
handlers:
rollbar:
type: rollbar
level: error
token: %rollbar_token%
Your question has two parts:
Rollbar
person_fn is exactly what you need. You should be able to add a reference to the function by using a string (e.g.: "MyClass::static_function_reference" or "my_function_name").
Symfony
Disclaimer: I don't use or know much about Symfony.
This question has some excellent examples of how to get the current user in Symfony. (Punch line: in a controller you can call $this.getUser())
This question has a good example of how to inject the current user in a service. (Make a Twig Extension that depends on the SecurityContext or TokenStorage, use those dependencies to get a user objet).
Finally, there's the classic PHP move: as soon as you have a user add it to $_REQUEST. I'm not sure if Symfony co-opts this, but it'd be a valid way in a non-framework PHP application.

Pre_deserialize callback not working in JMSSerializer

I am trying to just execute my Document's __constructor on pre_deserialization via jmsserializer but I don't have a clue why it is not working.
I am loading the serializer metadata from a yaml file looking like this:
AppBundle\Document\Campaign:
exclusion_policy: ALL
xml_root_name: campaign
properties:
id:
type: string
expose: true
slug:
type: string
expose: true
name:
type: string
expose: true
callback_methods:
pre_deserialize: [__construct]
When I try to deserialize executing:
$object = $serializer->deserialize($jsonString, 'AppBundle\\Document\\Campaign', 'json');
I am unable to reach the contructor function, however If I change the event to any of the others available (pre_serialize, post_serialize and post_deserialize) I do.
I think there are missing code about the handling of this specific event but trying to copy the same code affecting the other events it still not working.
It looks like it is never registered in the event dispatcher or something similar.
My environment is:
symfony 2.6.3
jms/serializer 0.16.0
jms/serializer-bundle 0.13.0
Thanks.
I can verify this appears to be a bug in JMS Serializer. For some reason, the service container is not reading the pre_deserialize events and registering it with JMS.
You can, however, work around this using an event subscriber.
First define the Subscriber class, similar to your listener:
<?php
namespace Acme\AcmeBundle\Listener;
use JMS\Serializer\EventDispatcher\PreDeserializeEvent;
use JMS\Serializer\EventDispatcher\EventSubscriberInterface;
class SerializationSubscriber implements EventSubscriberInterface
{
/**
* #inheritdoc
*/
static public function getSubscribedEvents()
{
return array(
array('event' => 'serializer.pre_deserialize', 'method' => 'onPreDeserialize'),
);
}
public function onPreDeserialize(PreDeserializeEvent $event)
{
echo "we're about to de-cerealizing";
}
}
Then register the Subscriber in your bundle's services configuration:
parameters:
acme.serializer_subscriber.class: Acme\AcmeBundle\Listener\SerializationSubscriber
services:
acme.serializer.subscriber:
class: %acme.serializer_subscriber.class%
tags:
- { name: jms_serializer.event_subscriber }
Rebuild your cache, and you should be good!
Official Documentation: http://jmsyst.com/libs/serializer/master/event_system

Resources