How configure bundle based on other service? - symfony

I want to configure my index types of Elastica dynamically based on entity list which I can get from entity repository. To do this I must have instance of repository so doctrine EntityManager must exist so Depedency Inject container must by compiled. But when it is compiled is too late for configure Elastica because container is frozen.
This is what i tried:
#config.yml
fos_elastica:
clients:
default: { host: 127.0.0.1, port: 9200 }
indexes:
app:
types: %elastic_types%
#application bundle
public function build(ContainerBuilder $container){
$types = array(
'tag' => array(
'mappings' => array(
'text' => array()
),
'persistence' => array(
'driver' => 'orm',
'model' => 'Cloud\AdmBundle\Entity\Tag',
'provider' => array(),
'listener' => array(),
'finder' => array()
)
)
);
$container->setParameter('elastic_types', $types);
}
It works but i don't have capabilities to use EntityRepository here.
#application bundle
public function setContainer(ContainerInterface $container = null) {
parent::setContainer($container);
$container->setParameter('elastic_types', array('anything param'));
}
Here is too late to set any params because container is compiled and i get execption:
ParameterNotFoundException in ParameterBag.php line 106:
You have requested a non-existent parameter "elastic_types".
How can i do this ?

Related

symfony4 use .env config variables in a service

I am using a package that is not especially made for symfony (TNTsearch), and have put all the functions I want to use in a service I called TNTsearchHelper.php. This service requires some variables, including some that could be found in the .env file. I currently define and construct these in my class:
class TntSearchHelper
{
public function __construct(EntityManagerInterface $em)
{
$this->em = $em;
$config = [
'driver' => 'mysql',
'host' => 'localhost',
'database' => 'databasename',
'username' => 'user',
'password' => 'pw',
'storage' => 'my/path/to/file',
'charset' => 'utf8',
'collation' => 'utf8_general_ci',
];
$this->config = $config;
}
What I would really like is to simply use the variables for my database that are set in the .env file. Is there any way to do this? This service is not registered in services.yaml because this is not neccesary with the autowire: true option, so I don't have any config options/file for my service in the config and wonder if I can keep it that way.
Yes. It's possible. If you want to use env variables for configuration, you have two options:
1.Use getenv:
$config = [
'driver' => 'mysql',
'host' => getenv('MYSQL_HOST'),
'database' => getenv('MYSQL_DB'),
'username' => getenv('MYSQL_LOGIN'),
'password' => getenv('MYSQL_PASSWORD'),
'storage' => 'my/path/to/file',
'charset' => 'utf8',
'collation' => 'utf8_general_ci',
];
2.Configure your service in services.yaml:
services:
App\TntSearchHelper:
arguments:
- '%env(MYSQL_HOST)%'
- '%env(MYSQL_DB)%'
- '%env(MYSQL_LOGIN)%'
- '%env(MYSQL_PASSWORD)%'
And change your __construct function to this:
public function __construct(string $host, string $db, string $login, string $password, EntityManagerInterface $em)
{
$this->em = $em;
$config = [
'driver' => 'mysql',
'host' => $host,
'database' => $db,
'username' => $login,
'password' => $password,
'storage' => 'my/path/to/file',
'charset' => 'utf8',
'collation' => 'utf8_general_ci',
];
$this->config = $config;
}
Also make sure that all this env variables are set because there's only DATABASE_URL variable in .env file by default
I know three possibilities. Each case has 03 steps configuration : 1 - declare yours variables in env. 2 - config service file 3 - and call your parameter
_ In controllers extending from the AbstractController, and use the getParameter() helper :
YAML file config
# config/services.yaml
parameters:
kernel.project_dir: "%env(variable_name)%"
app.admin_email: "%env(variable_name)%"
In your service,
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
class UserController extends AbstractController
{
// ...
public function index(): Response
{
$projectDir = $this->getParameter('kernel.project_dir');
$adminEmail = $this->getParameter('app.admin_email');
// ...
}
}
_ In services and controllers not extending from AbstractController, inject the parameters as arguments of their constructors.
YAML file config
# config/services.yaml
parameters:
app.contents_dir: "%env(variable_name)%"
services:
App\Service\MessageGenerator:
arguments:
$contentsDir: '%app.contents_dir%'
In your service,
class MessageGenerator
{
private $params;
public function __construct(string $contentsDir)
{
$this->params = $contentsDir;
}
public function someMethod()
{
$parameterValue = $this->params;
// ...
}
}
_ Finally, if some service needs access to lots of parameters, instead of injecting each of them individually, you can inject all the application parameters at once by type-hinting any of its constructor arguments with the ContainerBagInterface:
YAML file config
# config/services.yaml
parameters:
app.parameter_name: "%env(variable_name)%"
In your service,
use Symfony\Component\DependencyInjection\ParameterBag\ContainerBagInterface;
class MessageGenerator
{
private $params;
public function __construct(ContainerBagInterface $params)
{
$this->params = $params;
}
public function someMethod()
{
$parameterValue = $this->params->get('app.parameter_name');
// ...
}
}
source Accessing Configuration Parameters

Class does not exist symfony

I'm having a problem recovering my entities, the entities are in the AppBundle / Entity folder, but symfony can not find it ...
Here is the error: Class 'Product' does not exist
Here is the function myManager () present in a controller
public function myManager(){
$isDevMode = true;
$config = Setup::createAnnotationMetadataConfiguration(array(__DIR__."/AppBundle/Entity"), $isDevMode);
// database configuration parameters
$conn = array(
'dbname' => 'teste',
'user' => 'root',
'password' => '',
'host' => '127.0.0.1',
'driver' => 'pdo_mysql',
);
$entityManager = EntityManager::create($conn, $config);
return $entityManager;
}
the function testAction () that calls the manager and tries to load the Product entity
public function testAction(){
$em = $this->myManager()->getRepository('Product');
return $this->render('toto.html.twig');
}
link of documentation : Doctrine
You need to use the correct notation to make a reference to your entity:
$manager->getRepository('MyBundleName:Product')

JMS Serializer event is not working

I am sure it is a small error but I cannot find it.
I am trying to follow the official doc and implement an event listener on the pre_serialize event.
My service declaration:
<service id="app.question_serializer_subscriber" class="AppBundle\Serializer\QuestionSerializerSubscriber">
<tag name="jms_serializer.event_subscriber"/>
</service>
My subscriber:
<?php
namespace AppBundle\Serializer;
use JMS\Serializer\EventDispatcher\EventSubscriberInterface;
use JMS\Serializer\EventDispatcher\ObjectEvent;
class QuestionSerializerSubscriber implements EventSubscriberInterface
{
public static function getSubscribedEvents()
{
return array(
array(
'event' => 'serializer.pre_serialize',
'method' => 'onPreSerialize',
)
);
}
public function onPreSerialize(ObjectEvent $event)
{
die('in event');
}
}
And my controller:
$question = $repo->findLastVersionByQuestionId((int) $questionId);
$serializer = SerializerBuilder::create()->build();
$context = new SerializationContext();
return new JsonResponse(json_decode(
$serializer->serialize(
$question,
'json',
$context
),
true
));
When I access the route my entity Question is serialized and displayed, but why does the die('in event'); is not displayed ?
Maybe it has a relation with the fact that my object is a Doctrine entity (issue 666 or PR 677 )
I finally find the issue. The problem is
$serializer = SerializerBuilder::create()->build();
This does not work but this does:
$serializer = $this->get('jms_serializer');
Try adding the class attribute, as example:
public static function getSubscribedEvents()
{
return array(
array(
'event' => 'serializer.pre_serialize',
'class' => 'FQCN_class_name',
'method' => 'onPreSerialize',
)
);
}
Another difference regarding the doc is in the argument of the class method: you should use PreSerializeEvent instead of ObjectEvent:
So like this:
public function onPreSerialize(PreSerializeEvent $event)
{
// ...
}
Check your service is correctly load from the container as example with the console command:
php app/console debug:container --tag=jms_serializer.event_subscriber
Hope this help

Custom Handler on JMSSerializerBundle is ignored

I am attempting to use a custom handler for JMS Serializer Bundle
class CustomHandler implements SubscribingHandlerInterface
{
public static function getSubscribingMethods()
{
return array(
array(
'direction' => GraphNavigator::DIRECTION_SERIALIZATION,
'format' => 'json',
'type' => 'integer',
'method' => 'serializeIntToJson',
),
);
}
public function serializeIntToJson(JsonSerializationVisitor $visitor, $int, array $type, Context $context)
{
die("GIVE ME SOMETHING");
}
}
This does nothing, and does not die. This is how I am registering the handler
$serializer = SerializerBuilder::create()
->configureHandlers(function(HandlerRegistry $registry) {
$registry->registerSubscribingHandler(new MyHandler());
})
->addDefaultHandlers()
->build();
$json = $serializer->serialize($obj, 'json');
My handler is never called and I cannot manipulate the data on serialisation.
You need to create a service for this handler:
custom_jms_handler:
class: MyBundle\Serializer\CustomHandler
tags:
- { name: jms_serializer.subscribing_handler }
Then make sure you use the registered JMS serializer service
$json = $this->get('jms_serializer')->serialize($obj, 'json');
I have this which works
$serializer = SerializerBuilder::create()
->configureListeners(function(EventDispatcher $dispatcher) {
$dispatcher->addSubscriber(new ProjectSubscriber($this->container));
$dispatcher->addSubscriber(new UserSubscriber($this->container));
})
->addDefaultListeners()
->addMetadataDir(realpath($this->get('kernel')->getRootDir()."/../") . '/src/Jake/NameOfBundle/Resources/config/serializer')
->build();
return $serializer->serialize($project, 'json');
$project is my entity.
You can omit this line if you don't have serializer configs
->addMetadataDir(realpath($this->get('kernel')->getRootDir()."/../") . '/src/Jake/NameOfBundle/Resources/config/serializer')
I think my main issue was this ->addDefaultListeners().
In config.yml I have
jms_serializer:
metadata:
auto_detection: true
directories:
NameOfBundle:
namespace_prefix: ""
path: "#JakeNameOfBundle/Resources/config/serializer"
I don't have anthing set up to make JMS a service.

Getting Symfony \ Component \ HttpKernel \ Exception \ MethodNotAllowedHttpException

I'm sending a PUT request that handles input data and updates a record, but I get the above response. The problem doesn't seem to be the route, however, because if I do dd($user) after the $user = User::whereId($id)->firstOrFail(); line, I get the object returned correctly.
Yet, when it comes time to validate it, it throws this error.
# routes
Route::resource('users', 'UsersController', ['only' => ['index', 'show', 'update']]);
# api call
PUT /users/2
# controller
public function update($id)
{
$user = User::whereId($id)->firstOrFail();
$input = Input::all();
$this->userForm->validate($input);
$user->fill($input)->save();
return $user->toJson();
}
# userForm class
<?php namespace Olp\Forms;
use Laracasts\Validation\FormValidator;
class UserForm extends FormValidator {
protected $rules = [
'email' => 'required|email|unique:users',
'password' => 'required',
'password_confirmation' => 'required|same:password',
'firstname' => 'required',
'lastname' => 'required',
'schoolname' => 'required',
'address1' => 'required',
'address2' => 'required',
'postcode' => 'required',
'city' => 'required'
];
}
and in my UserController:
use Olp\Forms\UserForm;
class UsersController extends \BaseController {
function __construct(UserForm $userForm)
{
$this->userForm = $userForm;
}
I'm not a Laravel guy, but a quick check on their documentation indicates that Resource Controllers support PUT in addition to other HTTP verbs.
I was not able to figure out how to add HTTP verb support to an arbitrary action, but this indicates that you can name a controller action prefixed with the verb it responds to
public function putUpdate($id)

Resources