FOS Elastica manual provider service injection issue - php

I am trying to create a manual provider to manually populate my FOS Elastica index to account for some complex joins. At the moment, I am just trying to get the provider to work even without the joins, and I am having trouble injecting the correct Elastica Type into my constructor for the provider. Here is the constructor of my provider:
// ...
class EmpPosDeptProvider implements ProviderInterface
{
private $em;
protected $type;
public function __construct(Type $type, EntityManager $em)
{
$this->type = $type;
$this->em = $em->getRepository('CcitEmployeesBundle:Position');
}
// ...
and here is my services.yml file:
services:
employees.search_provider.empPosDept:
class: Ccit\EmployeesBundle\Search\EmpPosDeptProvider
tags:
- { name: fos_elastica.provider, index: employees, type: empPosDept }
arguments:
- %fos_elastica.type.class%
- #doctrine.orm.entity_manager
When I try to execute php app/console fos:elastica:populate I am receiving the following error:
PHP Catchable fatal error: Argument 1 passed to Ccit\EmployeesBundle\Search
\EmpPosDeptProvider::__construct() must be an instance of Elastica\Type, string given,
called in /vagrant-nfs/employees/app/cache/dev/appDevDebugProjectContainer.php on line 736
and defined in /vagrant-nfs/employees/src/Ccit/EmployeesBundle/Search
/EmpPosDeptProvider.php on line 23
Does anyone know what I need to give as a correct argument in my services.yml file? Or could the problem be something else entirely?

You're passing a string containing Ccit\EmployeesBundle\Search\EmpPosDeptProvider. You have to pass an instance of EmpPosDeptProvider, and it may be declared in your services.yml something like:
services:
fos_elastica.type:
class: %fos_elastica.type.class%
employees.search_provider.empPosDept:
class: Ccit\EmployeesBundle\Search\EmpPosDeptProvider
tags:
- { name: fos_elastica.provider, index: employees, type: empPosDept }
arguments:
- #fos_elastica.type
- #doctrine.orm.entity_manager

Apparently I needed to provide the explicit path to the type I was referencing. The following line worked:
#fos_elastica.index.employees.employeePositionDepartment
This makes sense given that my config.yml file contains the following:
fos_elastica:
clients:
default: { host: localhost, port: 9200 }
indexes:
employees:
client: default
types:
employeePositionDepartment:
mappings:
id: { type: integer }
title: { type: string }
startDate: { type: date, format: date_time_no_millis }
endDate: { type: date, format: date_time_no_millis }
supervisor: { type: integer }
Thanks to anyone who was considering helping me with this rather elementary question.

Related

Symfony: change database dynamically

let's say I have 3 databases:
prefix_db1
prefix_db2
prefix_db3
And I want to connect to them dynamically from the url like this http://localhost/my-project/web/app_dev.php/db1/books so I know which database to conenct to from the url (in this case prefix_db1)
And basically the idea was to prepare a listener that will be fired with each http request, get the database name from the url and then override doctrin's params, something like this:
Within services.yml:
dynamic_connection:
class: AppBundle\service\DynamicDBConnector
arguments: ['#request_stack']
calls:
- [ setDoctrineConnection, ['#doctrine.dbal.default_connection'] ]
tags:
- { name: kernel.event_listener, event: kernel.request, method: onKernelRequest }
My listener:
<?php
namespace AppBundle\service;
use Doctrine\DBAL\Connection;
use Symfony\Component\HttpFoundation\RequestStack;
use Exception;
class DynamicDBConnector
{
/**
* #var Connection
*/
private $connection;
/*
* #var Request
*/
private $request;
public function __construct(RequestStack $requestStack)
{
$this->request = $requestStack->getCurrentRequest();
}
/**
* Sets the DB Name prefix to use when selecting the database to connect to
*
* #param Connection $connection
* #return DynamicDBConnector $this
*/
public function setDoctrineConnection(Connection $connection)
{
$this->connection = $connection;
return $this;
}
public function onKernelRequest()
{
if ($this->request->attributes->has('_company')) {
$connection = $this->connection;
$params = $this->connection->getParams();
$companyName = $this->request->get('_company');
// I did the concatenation here because in paramaters.yml I just put the prefix (database_name: prefix_) so after the concatenation I get the whole database name "prefix_db1"
$params['dbname'] = $params['dbname'] . $companyName;
// Set up the parameters for the parent
$connection->__construct(
$params,
$connection->getDriver(),
$connection->getConfiguration(),
$connection->getEventManager()
);
try {
$connection->connect();
} catch (Exception $e) {
// log and handle exception
}
}
return $this;
}
}
Now this worked very well I have tested it using a simple list of books and each time I change the url I get the list related to each database:
http://localhost/my-project/web/app_dev.php/db1/books // I get books of database prefix_db1
http://localhost/my-project/web/app_dev.php/db2/books // I get books of database prefix_db2
Now let's get to the problem shall we :):
The problem now is that when I secure my project with authentication system and try to login (of course each database has user table) using this url http://localhost/my-project/web/app_dev.php/db1/login
I get this exception :
An exception occured in driver: SQLSTATE[HY000] [1049] Base 'prefix_' unknown
As you can see symfony tried to login the user using the database_name declared in parameters.yml which means that the security_checker of symfony has been fired before my listener and before overriding Doctrine's params.
My question:
Is there any way to fire my listener before any other http request listener ? or maybe an alternative solution to make sure that any request to database must be with the right database name.
Sorry for the long post.
EDIT:
From the official documentation of symfony:
https://symfony.com/doc/2.3/cookbook/event_dispatcher/event_listener.html
The other optional tag attribute is called priority, which defaults
to 0 and it controls the order in which listeners are executed (the
highest the priority, the earlier a listener is executed). This is
useful when you need to guarantee that one listener is executed before
another. The priorities of the internal Symfony listeners usually
range from -255 to 255 but your own listeners can use any positive or
negative integer.
I set the priority of my listener to 10000:
tags:
- { name: kernel.event_listener, event: kernel.request, method: onKernelRequest, priority: 10000 }
But the problem persist, still can't fire my listener before symfony!
I found a solution
The idea is tochange the default Connection class that symfony uses to create a database connection:
doctrine:
dbal:
connections:
default:
wrapper_class: AppBundle\Doctrine\DynamicConnection
driver: pdo_mysql
host: '%database_host%'
port: '%database_port%'
dbname: '%database_name%'
user: '%database_user%'
password: '%database_password%'
charset: UTF8
After that we can change the given params in the constructor:
use Doctrine\DBAL\Connection;
use Doctrine\DBAL\Driver;
class DynamicConnection extends Connection
{
public function __construct(array $params, Driver $driver, $config, $eventManager)
{
$params['dbname'] = 'teqsdqsdqst';
parent::__construct($params, $driver, $config, $eventManager);
}
}
Now we just need to get the parameter from the url and set inside $params['dbname'].
In this way we make sure that symfony will always use this class to create the connection and we no longer need to fire listeners with http requestes
Great solution but if you want get the parameter _company from the URL you can retrieve the container inside the constructor through the EventManager object passed in parameters and get the current request from it, in fact the container is injected into ContainerAwareEventManager the sub class of EventManager
class DynamicDBConnector extends Connection
{
public function __construct($params, $driver, $config, $eventManager)
{
if(!$this->isConnected()){
// Create default config and event manager if none given (case in command line)
if (!$config) {
$config = new Configuration();
}
if (!$eventManager) {
$eventManager = new EventManager();
}
$refEventManager = new \ReflectionObject($eventManager);
$refContainer = $refEventManager->getProperty('container');
$refContainer->setAccessible('public'); //We have to change it for a moment
/*
* #var \Symfony\Component\DependencyInjection\ContainerInterface $container
*/
$conrainer = $refContainer->getValue($eventManager);
/*
* #var Symfony\Component\HttpFoundation\Request
*/
$request = $conrainer->get('request_stack')->getCurrentRequest();
if ($request != null && $request->attributes->has('_company')) {
$params['dbname'] .= $request->attributes->get('_company');
}
$refContainer->setAccessible('private'); //We put in private again
parent::__construct($params, $driver, $config, $eventManager);
}
}
}
you should add the database name in your config.yml like this :
orm:
auto_generate_proxy_classes: '%kernel.debug%'
# naming_strategy: doctrine.orm.naming_strategy.underscore
# auto_mapping: true
default_entity_manager: default
entity_managers:
default:
connection: default
mappings:
DataMiningBundle: ~
AppBundle: ~
UserBundle: ~
your_second_db:
connection: your_second_db (decalared in parameters.yml)
mappings:
yourBundle: ~
and call it from your controller :
$em = $doctrine->getConnection('your_second_db');

Type error: Argument 1 passed to AppBundle\EventListener\CalendarEventListener::__construct() must be an instance of Doctrine\ORM\EntityManager

so i'm new into Symfony and i've been trying to create an app using https://github.com/adesigns/calendar-bundle .
I've parsed all steps into creating the CalendarEventListener but i've got an error:
FatalThrowableError in CalendarEventListener.php line 13: Type error: Argument 1 passed to AppBundle\EventListener\CalendarEventListener::__construct() must be an instance of Doctrine\ORM\EntityManager, instance of Symfony\Bundle\TwigBundle\TwigEngine given, called in /home/intern/Desktop/newapp/var/cache/dev/appDevDebugProjectContainer.php on line 1825
I have tried to fix by researching similar questions but i didn't figured it out.
My EventCalendarListener:
<?php
namespace AppBundle\EventListener;
use ADesigns\CalendarBundle\Event\CalendarEvent;
use ADesigns\CalendarBundle\Entity\EventEntity;
use Doctrine\ORM\EntityManager;
class CalendarEventListener
{
private $entityManager;
public function __construct(EntityManager $entityManager)
{
$this->entityManager = $entityManager;
}
public function loadEvents(CalendarEvent $calendarEvent)
{
$startDate = $calendarEvent->getStartDatetime();
$endDate = $calendarEvent->getEndDatetime();
// The original request so you can get filters from the calendar
// Use the filter in your query for example
$request = $calendarEvent->getRequest();
$filter = $request->get('filter');
// load events using your custom logic here,
// for instance, retrieving events from a repository
$companyEvents = $this->entityManager->getRepository('AppBundle:MyCompanyEvents')
->createQueryBuilder('company_events')
->where('company_events.event_datetime BETWEEN :startDate and :endDate')
->setParameter('startDate', $startDate->format('Y-m-d H:i:s'))
->setParameter('endDate', $endDate->format('Y-m-d H:i:s'))
->getQuery()->getResult();
// $companyEvents and $companyEvent in this example
// represent entities from your database, NOT instances of EventEntity
// within this bundle.
//
// Create EventEntity instances and populate it's properties with data
// from your own entities/database values.
foreach($companyEvents as $companyEvent) {
// create an event with a start/end time, or an all day event
if ($companyEvent->getAllDayEvent() === false) {
$eventEntity = new EventEntity($companyEvent->getTitle(), $companyEvent->getStartDatetime(), $companyEvent->getEndDatetime());
} else {
$eventEntity = new EventEntity($companyEvent->getTitle(), $companyEvent->getStartDatetime(), null, true);
}
//optional calendar event settings
$eventEntity->setAllDay(true); // default is false, set to true if this is an all day event
$eventEntity->setBgColor('#FF0000'); //set the background color of the event's label
$eventEntity->setFgColor('#FFFFFF'); //set the foreground color of the event's label
$eventEntity->setUrl('http://www.google.com'); // url to send user to when event label is clicked
$eventEntity->setCssClass('my-custom-class'); // a custom class you may want to apply to event labels
//finally, add the event to the CalendarEvent for displaying on the calendar
$calendarEvent->addEvent($eventEntity);
}
}
}
also my services.yml
parameters:
# parameter_name: value
services:
app.form_login_authenticator:
class: AppBundle\Security\FormLoginAuthenticator
arguments: ["#router", "#security.password_encoder"]
kernel.listener.allotaxi_exception_listener:
class: AppBundle\EventListener\CalendarEventListener
arguments: ["#templating", "#kernel",]
tags:
- { name: kernel.event_listener, event: kernel.exception, method: onKernelException }
services.xml
<?xml version="1.0" ?>
<container xmlns="http://symfony.com/schema/dic/services"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
<parameters>
<parameter key="fullcalendar.event.class">ADesigns\CalendarBundle\Entity\EventEntity</parameter>
<parameter key="fullcalendar.loader.event">calendar.load_events</parameter>
</parameters>
</container>
in config.yml all services are imported
Any help will be appreciated :)
You are sending 2 arguments to your event listener, but in the construct you need entity manager. You should add entity manager under the arguments, like this:
kernel.listener.allotaxi_exception_listener:
class: AppBundle\EventListener\CalendarEventListener
arguments: ["#doctrine.orm.entity_manager"]
tags:
- { name: kernel.event_listener, event: kernel.exception, method: onKernelException }
Thanks for replies. Indeed was a problem with arguments passed through services.yml and besides that i had to pass some calendar event tags for the orm entity manager as in the code below.
kernel.listener.allotaxi_exception_listener:
class: AppBundle\EventListener\CalendarEventListener
arguments: ["#doctrine.orm.entity_manager"]
tags:
- { name: kernel.event_listener, event: calendar.load_events, method: loadEvents }

How to store and read values in cache using Symfony and RedisAdapter?

I am trying to store some values on cache the first time I load a page. This is the code I am using:
$cached_items = [
'main_nav' => $main_nav,
'sub_nav' => $sub_nav,
'footer_nav' => $footer_nav,
'view_as' => $view_as,
];
$redisConnection = new Client('tcp://redis:6379');
$cache = new RedisAdapter($redisConnection);
$menu = $cache->getItem('mmi_menus');
if ($menu->isHit()) {
return $menu->get();
} else {
$menu->set($cached_items);
$cache->save($menu);
}
This caching is being done from a non Symfony controller - let's say it's a standalone file.
First problem with the code above,
the else condition is reach out all the time and I think it should not be since values are stored. (check here)
Second problem, having this function in a Symfony controller:
public function GenerateMenuItemsAction()
{
$redisConnection = new Client('tcp://redis:6379');
$cache = new RedisAdapter($redisConnection);
$menu = $cache->getItem('mmi_menus');
if ($menu->isHit()) {
return $this->render(
'CommonBundle:Layout:menu.html.twig',
['menu' => $menu->get()]
);
}
}
$menu->isHit() is null so all the time I am getting this exception from Symfony:
An exception has been thrown during the rendering of a template ("The
controller must return a response (null given). Did you forget to add
a return statement somewhere in your controller?").
Update
I am not using any TTL afaik maybe somehow a default one is setup but this is how the section looks like on the config.yml:
framework:
cache:
app: cache.adapter.redis
default_redis_provider: "redis://%redis_host%"
pools:
cache.pool1:
public: true
What I am missing here? Any ideas?
my config.yml looks like that:
framework:
cache:
system: cache.adapter.apcu
default_redis_provider: redis://%redis_password%#%redis_host%:%redis_port%
pools:
redis_pool:
adapter: cache.adapter.redis
public: true
default_lifetime: 0
provider: cache.default_redis_provider
So I can easily (in my Controller) do something like:
$this->get('redis_pool')->getItem('myitem');
Or you can inject 'redis_pool' as an argument to a Service.
I don't need any 'new' or extra Connection information/configuration - anything is done in config.yml and available as a Service across the application.

RabbitMQBundle does not acknowledge message when producing message from consumer

How to acknowledge the message and produce a new message at the same time?
When I start my consumer from the command line, the messages will stay in the original queue. But new ones will be created in the new queue, in an infinite loop. Because it keeps consuming the messages that are not being acknowledged.
Even though TRUE is returned in the execute() function of the consumer. Which should acknowledge it, like it says in the documentation.
I am producing messages from a callback inside a consumer. This producer is injected using the standard Symfony DI.
If I remove the method that publishes the new message, the messages are acknowledged just fine...
services.yml
services:
my_importlog_repository:
class: Doctrine\ORM\EntityRepository
factory_service: doctrine.orm.default_entity_manager
factory_method: getRepository
arguments: [AppBundle\Entity\MyImportlogEntity]
my_distributor:
class: AppBundle\DistributorImport\MyDistributor
arguments: [#my_importlog_repository,#logger,#old_sound_rabbit_mq.my_download_producer, %my_config%]
my_download:
class: AppBundle\Consumer\MyDownloadConsumer
arguments: [#logger,#old_sound_rabbit_mq.my_extract_producer,#my_distributor,%my_config%]
my_extract:
class: AppBundle\Consumer\MyExtractConsumer
arguments: [#logger,#old_sound_rabbit_mq.my_convert_producer,#my_distributor,%my_config%]
config.yml
# rabbitmq
old_sound_rabbit_mq:
connections:
default:
host: '192.168.99.100'
port: 5672
user: 'guest'
password: 'guest'
vhost: '/'
lazy: false
connection_timeout: 60
read_write_timeout: 60
# requires php-amqplib v2.4.1+ and PHP5.4+
keepalive: false
# requires php-amqplib v2.4.1+
heartbeat: 30
producers:
# my producers
my_download:
connection: default
exchange_options: {name: 'distributor_import', type: direct}
queue_options: {name: 'my_download'}
my_extract:
connection: default
exchange_options: {name: 'distributor_import', type: direct}
queue_options: {name: 'my_extract'}
my_convert:
connection: default
exchange_options: {name: 'distributor_import', type: direct}
queue_options: {name: 'my_convert'}
consumers:
# my consumers
my_download:
connection: default
exchange_options: {name: 'distributor_import', type: direct}
queue_options: {name: 'my_download'}
callback: my_download
qos_options: {prefetch_size: 0, prefetch_count: 1, global: false}
idle_timeout: 60
my_extract:
connection: default
exchange_options: {name: 'distributor_import', type: direct}
queue_options: {name: 'my_extract'}
callback: my_extract
qos_options: {prefetch_size: 0, prefetch_count: 1, global: false}
idle_timeout: 60
MyDownloadConsumer.php
<?php
namespace AppBundle\Consumer;
use OldSound\RabbitMqBundle\RabbitMq\ConsumerInterface;
class MyDownloadConsumer implements ConsumerInterface
{
private $logger;
private $producer;
private $distributor;
private $config;
public function __construct(\Symfony\Component\HttpKernel\Log\LoggerInterface $logger, \OldSound\RabbitMqBundle\RabbitMq\Producer $producer, \AppBundle\DistributorImport\MyDistributor $distributor, Array $config)
{
$this->logger = $logger;
$this->producer = $producer;
$this->distributor = $distributor;
$this->config = $config;
}
public function execute(\PhpAmqpLib\Message\AMQPMessage $message)
{
$data = unserialize($message->body);
$this->producer->publish(serialize($data));
return true;
}
}
If I remove
$data = unserialize($message->body);
$this->producer->publish(serialize($data));
It works like it should...
Was able to publish a message from inside my Consumer execute() method, while also acknowledging the current message being consumed. Using the following code.
$message->delivery_info['channel']
->basic_publish(
new AMQPMessage (serialize($data)),
'name_of_my_exchange',
'key.of.my.routing'
);
Publishing directly on the channel of the message that is being consumed.

OneupUploaderBundle(blueimp) - save file

I have problem with configure blueimp. When I send file (click: start upload) in jsconsole is:
POST XXXapp_dev.php/_uploader/gallery/upload 500 (Internal Server
Error)
I used:
Simple example of the entity file to be uploaded with OneupUploaderBundle
https://github.com/1up-lab/OneupUploaderBundle/issues/51#issuecomment-24878715
My file:
services.yml:
Cms.upload_listener:
class: Cms\AdminBundle\EventListener\UploadListener
arguments: [#doctrine]
tags:
- { name: kernel.event_listener, event: oneup_uploader.post_upload, method: onUpload }
config.yml:
oneup_uploader:
mappings:
gallery:
frontend: blueimp
storage:
service: ~
type: filesystem
filesystem: ~
directory: web/uploads/gallery
stream_wrapper: ~
sync_buffer_size: 100K
routing.yml
oneup_uploader:
resource: .
type: uploader
~Entity/File.php
/* ... */
/**
* #ORM\Entity
* #ORM\Table(name="file")
*/
class file {
/**
* #ORM\Column(type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #ORM\Column(type="string")
*/
protected $filename;
}
~/EventListner/UploadListner.php
class UploadListener
{
protected $manager;
public function __construct(EntityManager $manager)
{
$this->manager = $manager;
}
public function onUpload(PostPersistEvent $event)
{
$file = $event->getFile();
$object = new file();
$object->setFilename($file->getPathName());
$this->manager->persist($object);
$this->manager->flush();
}
}
debug route:
_uploader_upload_gallery POST ANY ANY
/_uploader/gallery/upload
Directory /web/uploads/gallery is on the server.
What am I trying to achieve? I need upload files to server and add record to db.
I think that config is incorrect, but where?
Edit 1:
Maybe help
INFO - An exception was thrown while getting the uncalled listeners
(Catchable Fatal Error: Argument 1 passed to
Cms\AdminBundle\EventListener\UploadListener::__construct() must be an
instance of Cms\AdminBundle\EventListener\EntityManager, instance of
Doctrine\Bundle\DoctrineBundle\Registry given, called in
/home/cms/public_html/app/cache/dev/appDevDebugProjectContainer.php on
line 455 and defined) Context:
{"exception":"Object(Symfony\Component\Debug\Exception\ContextErrorException)"}
CRITICAL - Uncaught PHP Exception
Symfony\Component\HttpFoundation\File\Exception\FileException: "Unable
to create the "/uploads/gallery" directory" at
/home/cms/public_html/vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/File/File.php
line 134 Context:
{"exception":"Object(Symfony\Component\HttpFoundation\File\Exception\FileException)"}
I created uploads/gallery! chmod 777. I don't understand.
Edit 2:
Cms.upload_listener:
class: Cms\AdminBundle\EventListener\UploadListener
arguments: ["#doctrine.orm.entity_manager"]
tags:
- { name: kernel.event_listener, event: oneup_uploader.post_upload, method: onUpload }
and:
directory: ./uploads/gallery
Now created, uploaded file in "uploads/gallery/..."
But...
CRITICAL - Uncaught PHP Exception
Symfony\Component\Debug\Exception\ContextErrorException: "Catchable
Fatal Error: Argument 1 passed to
Cms\AdminBundle\EventListener\UploadListener::onUpload() must be an
instance of Oneup\UploaderBundle\Event\PostPersistEvent, instance of
Oneup\UploaderBundle\Event\PostUploadEvent given" at
/home/cms/public_html/src/Cms/AdminBundle/EventListener/UploadListener.php
line 17 Context:
{"exception":"Object(Symfony\Component\Debug\Exception\ContextErrorException)"}
Returns blueimp "Internal Server Error ..." and doesn't add records in the Entity.

Categories