I have multiple Symfony applications running on the same sever, and every application may need a different time zone.
For php, it's doable by using different fpm pools and setting the time zone in the pool configuration:
php_admin_value[date.timezone] = America/New_York
but for MySQL, I need to issue the statement:
"SET time_zone = 'America/New_York';
as the first query after connected, or add to the $options array in the PDO constructor:
PDO::MYSQL_ATTR_INIT_COMMAND => "SET time_zone = 'America/New_York';"
How can this be done?
You can use PostConnect Doctrine event ... https://www.doctrine-project.org/projects/doctrine-dbal/en/2.9/reference/events.html#postconnect-event
This is useful to configure the connection before any sql statement is executed.
An example may be:
<?php
namespace App\EventListener;
use Doctrine\DBAL\Event\ConnectionEventArgs;
/**
* My initializer
*/
class MyPdoInitializerListener
{
public function postConnect(ConnectionEventArgs $args)
{
$args->getConnection()
->exec("SET time_zone = 'America/New_York'");
}
}
Don't forget to add the listener to services.yaml
# services.yaml
# ...
App\EventListener\MyPdoInitializerListener:
tags:
- { name: doctrine.event_listener, event: postConnect }
To extend on SilvioQ's answer, I wrote the following snippet to inherit the time zone from the PHP environment, assuming that date_default_timezone_set() was previously called to set the correct time zone:
This solution has an advantage over named time zones (like "America/New_York") when those named time zones are not available within MySQL/MariaDB and you have no control over the server.
public function postConnect(ConnectionEventArgs $args) {
$tz = new DateTimeZone(date_default_timezone_get());
$offset = $tz->getOffset(new DateTime('now'));
$abs = abs($offset);
$str = sprintf('%s%02d:%02d', $offset < 0 ? '-' : '+', intdiv($abs, 3600), intdiv($abs % 3600, 60));
$args->getConnection()->exec("SET time_zone = '$str'");
}
As of version 3.5 of Doctrine DBAL, the solution proposed by SilvioQ is deprecated.
Doctrine documentation recommends implementing a middleware class for the database driver instead. An example of such implementation can be found within the DBAL code itself: https://github.com/doctrine/dbal/blob/3.5.x/src/Logging/Middleware.php
Related
Hey Im trying API Platform with Symfony 6.0 (and PHP 8)
Everything was going alright until I needed to make a DataPersister so I can encrypt the user password before saving it
I literally copied the example in the docs (here https://api-platform.com/docs/core/data-persisters/#decorating-the-built-in-data-persisters) since my entity is actually called User:
<?php
namespace App\DataPersister;
use ApiPlatform\Core\DataPersister\ContextAwareDataPersisterInterface;
use App\Entity\User;
final class UserDataPersister implements ContextAwareDataPersisterInterface
{
private $decorated;
public function __construct(ContextAwareDataPersisterInterface $decorated)
{
$this->decorated = $decorated;
}
public function supports($data, array $context = []): bool
{
return $this->decorated->supports($data, $context);
}
public function persist($data, array $context = [])
{
$result = $this->decorated->persist($data, $context);
return $result;
}
public function remove($data, array $context = [])
{
return $this->decorated->remove($data, $context);
}
}
I just removed the mailer parts cause what Im trying to do has nothing to do with that. Other than that, it is exactly equal to the example
But it wont work. I get this error when I try to persist:
Cannot autowire service "App\DataPersister\UserDataPersister": argument "$decorated" of method "__construct()" references interface "ApiPlatform\Core\DataPersister\ContextAwareDataPersisterInterface" but no such service exists. Try changing the type-hint to "ApiPlatform\Core\DataPersister\DataPersisterInterface" instead.
I tried doing what the error suggests but it seems to throw the framework in some endless loop or something cause I get a memory error. And in any case, I need a ContextAwareDataPersisterInterface
Am I doing something wrong or missing something here? Or this a bug? The docs says:
"If service autowiring and autoconfiguration are enabled (they are by default), you are done!"
They are both enabled in services.yaml:
# This file is the entry point to configure your own services.
# Files in the packages/ subdirectory configure your dependencies.
# Put parameters here that don't need to change on each machine where the app is deployed
# https://symfony.com/doc/current/best_practices.html#use-parameters-for-application-configuration
parameters:
services:
# default configuration for services in *this* file
_defaults:
autowire: true # Automatically injects dependencies in your services.
autoconfigure: true # Automatically registers your services as commands, event subscribers, etc.
# makes classes in src/ available to be used as services
# this creates a service per class whose id is the fully-qualified class name
App\:
resource: '../src/'
exclude:
- '../src/DependencyInjection/'
- '../src/Entity/'
- '../src/Kernel.php'
# add more service definitions when explicit configuration is needed
# please note that last definitions always *replace* previous ones
I works if I explicity define the service in services.yaml:
App\DataPersister\UserDataPersister:
bind:
$decorated: '#api_platform.doctrine.orm.data_persister'
edit: sorry, the documentation actually says we have to do that, I missed it. My bad.
Problem solved
I need to switch the Symfony cache adapter depending on ENV conditions. Like if some variable is set, use "cache.adapter.apcu" or use "cache.adapter.filesystem" otherwise.
Is it possible somehow? The documentation is not really helpful with it.
P.S.: It is not possible for us to do this via the creation of a whole new environment
Here is a basic example for a CacheAdapter which has adapters fed into it and then picking one based on a parameter (or alternatively envvar):
<?php
namespace App\Cache;
use Psr\Cache\CacheItemInterface;
use Psr\Cache\InvalidArgumentException;
use Psr\Container\ContainerInterface;
use Symfony\Component\Cache\Adapter\AdapterInterface;
use Symfony\Component\Cache\CacheItem;
use Symfony\Contracts\Service\ServiceSubscriberInterface;
use Symfony\Contracts\Service\ServiceSubscriberTrait;
class EnvironmentAwareCacheAdapter implements AdapterInterface, ServiceSubscriberInterface
{
use ServiceSubscriberTrait;
private string $environment;
public function __construct(string $environment)
{
$this->environment = $environment;
}
public function getItem($key)
{
return $this->container->get($this->environment)->getItem($key);
}
public function getItems(array $keys = [])
{
return $this->container->get($this->environment)->getItems($key);
}
// ...
}
This is how you would configure it:
services:
App\Cache\EnvironmentAwareCacheAdapter:
arguments:
$environment: '%kernel.environment%'
tags:
- { name: 'container.service_subscriber', key: 'dev', id: 'cache.app' }
- { name: 'container.service_subscriber', key: 'prod', id: 'cache.system' }
It's not the most elegant solution and is missing error handling and possibly a fallback. Basically, by adding tags with an appropriately named key and the alias to an existing cache as id, you can then refer to that cache with the key in your own adapter. So, depending on your environment you will pick either one. You can replace the key and the constructor argument with anything else you like. I hope that helps.
It seems like you can not set up your cache configuration to use a environment variable like so:
framework:
cache:
app: %env(resolve:CACHE_ADAPTER)%
It is the constraint of FrameworkBundle that provides the cache service. And this constraint will not be "fixed" (Using environment variables at compile time #25173).
To make it possible you need to make your own cache provider that can just pass all arguments to the needed cache provider. You will have access to environment variables at runtime and so you can use it as a proxy that knows what provider to use.
I have the following issue: I am working on a symfony (2.8) project which depends on the jmsserializerbundle (1.1).
When enabling the symfony-serializer alongside the jms-serializer package,
# app/config/config.yml
framework:
# ...
serializer: { enabled: true }
jms_serializer:
metadata:
#...
upon calling $this->get('serializer') or $this->get('jms_serializer') I only get the jms-serializer. This issue seems to have been resolved in jmsserializerbundle version 2.0: https://github.com/schmittjoh/JMSSerializerBundle/issues/558
Is there any way to solve this without updating jmsserializerbundle to 2.0?
Would there be any difference in performance compared to the normal symfony-serializer configuration, when wrapping a symfony-serializer in a custom service? like so:
<?php
use SomeCustomNormalizer;
use Symfony\Component\Serializer\Encoder\JsonEncode;
use Symfony\Component\Serializer\Normalizer\ObjectNormalizer;
use Symfony\Component\Serializer\Serializer;
use Symfony\Component\Serializer\SerializerInterface;
class SerializerService implements SerializerInterface
{
private $serializer;
public function __construct()
{
$this->serializer = new Serializer(
[new SomeCustomNormalizer(), new ObjectNormalizer()],
[new JsonEncode()]
);
}
public function serialize($data, $format, array $context = array())
{
# serialize
}
public function deserialize($data, $type, $format, array $context = array())
{
# deserialize
}
}
# SomeBundle/Resources/config/services.yml
serializer_service:
class: SomeBundle\SerializerService
The question regarding the performance came up for me because the existing jms configuration registers the jmsserializerbundle in the app kernel, which is not the case my custom service, which is just set up in services.yml.
Thanks in advance
Solution
As described below I just had to add one line to the jms-config:
# app/config/config.yml
jms_serializer:
enable_short_alias: false
metadata:
#...
Is there any way to solve this without updating jmsserializerbundle to 2.0?
JMS Serializer provides the option:
jms_serializer:
enable_short_alias: false
Would there be any difference in performance compared to the normal symfony-serializer configuration when wrapping a Symfony-serializer in a custom service? like so:
I guess not, the Symfony serializer is just 'another' service defined by the FrameworkBundle, a wrapper around the Serializer class with the normalizers and encoders injected.
If you create your own service (like in your example) it will be compiled by the service container as well. You can check the definition here: https://github.com/symfony/symfony/blob/v2.8.52/src/Symfony/Bundle/FrameworkBundle/Resources/config/serializer.xml
I recently created a new symfony project (3.1) with a dependency on graphaware/neo4j-php-ogm and neo4j/neo4j-bundle to manage my database.
Then I created a new Entity class named User with properties (login, password, ...) and I want to automatically set the current date before the flush event occurs (on preFlush).
I saw the PRE_FLUSH constant in neo4j-php-ogm/src/Events.php (https://github.com/graphaware/neo4j-php-ogm/blob/master/src/Events.php) but I haven't found any information about it in the documentation.
Well, my question is : Can we use this functionality in the actual version of the OGM ? If yes, do you have an example of the usage ?
Thank you for your help !
Yes you can, it is not documented you are right, I'll make sure it will be soon.
Integration test here : https://github.com/graphaware/neo4j-php-ogm/blob/master/tests/Integration/EventListenerIntegrationTest.php
First, You need create a class that will act as EventListener to the preFlush event of the EntityManager and a method reacting to the event :
<?php
namespace GraphAware\Neo4j\OGM\Tests\Integration\Listeners;
use GraphAware\Neo4j\OGM\Event\PreFlushEventArgs;
use GraphAware\Neo4j\OGM\Tests\Integration\Model\User;
class Timestamp
{
public function preFlush(PreFlushEventArgs $eventArgs)
{
$dt = new \DateTime("NOW", new \DateTimeZone("UTC"));
foreach ($eventArgs->getEntityManager()->getUnitOfWork()->getNodesScheduledForCreate() as $entity) {
if ($entity instanceof User) {
$entity->setUpdatedAt($dt);
}
}
}
}
Then you can register this event listener after having creating the entity manager :
/**
* #group pre-flush
*/
public function testPreFlushEvent()
{
$this->clearDb();
$this->em->getEventManager()->addEventListener(Events::PRE_FLUSH, new Timestamp());
$user = new User("ikwattro");
$this->em->persist($user);
$this->em->flush();
$this->assertNotNull($user->getUpdatedAt());
var_dump($user->getUpdatedAt());
}
Result of the test :
ikwattro#graphaware-team ~/d/g/p/ogm> ./vendor/bin/phpunit tests/ --group pre-flush
PHPUnit 5.6.2 by Sebastian Bergmann and contributors.
Runtime: PHP 5.6.27
Configuration: /Users/ikwattro/dev/graphaware/php/ogm/phpunit.xml.dist
. 1 / 1 (100%)int(1486763241)
Time: 378 ms, Memory: 5.00MB
OK (1 test, 1 assertion)
Result in the database :
Thank you a lot ! It's work perfectly. If anyone want to use it don't forget to type your property as "int". ;)
I'm currently implementing Doctrine filters in my Symfony2.1 project with the following setup:
<?php
namespace Acme\Bundle\Entity;
class Article {
/**
* #ORM\Column(type="string")
*/
private $status;
...
}
//app/config/config.yml
doctrine:
orm:
filters:
status:
class: Acme\Bundle\Filter\StatusFilter
enabled: false
....
//src/Acme/Bundle/Filter/StatusFilter.php
namespace Acme\Bundle\Filter;
use Acme\Bundle\Entity\Status;
class StatusFilter extends SQLFilter {
public function addFilterConstraint(ClassMetadata $target, $alias)
{
$filter =
$target->reflClass->implementsInterface('Acme\Bundle\Entity\Status')?
$alias . '.status = ' . Status::PUBLISHED : '';
return $filter;
}
}
Where Acme\Bundle\Entity\Status is just an interface.
The code is working as expected when the filter is enabled in config.yml.
The problem is that I cannot retrieve all articles for administration!
Is there a way to enable this filter for a certain bundle?
p.s. I know how to enable and disable the filter with the EntityManager,
I just cannot find the proper place to do it for the frontend Bundle.
my admin section is accessible by route prefix myadmin
www.example.com/myadmin/ -> admin section = disable filter (disabled by default in config)
www.example.com/... -> anything else = enable filter.
Looking at the Doctrine code, there are methods to enable and disable filters.
Once you have defined your filter in the config.yml file, you can enable/disable in a controller or service:
// 'status' is the unique name of the filter in the config file
$this->getDoctrine()->getManager()->getFilters()->enable('status');
$this->getDoctrine()->getManager()->getFilters()->disable('status');
Note: this was taken from Symfony 2.3. You would need to test this with previous versions of Symfony/Doctrine.
there is no notion of bundle at Doctrine level. The only way I see would be to detect which controller is used, by parsing its className (reflection, ...) during a kernel.request event, or a kernel.controller event.
Then, if you detect that your controller is in FrontendBundle, just disable/enable your doctrine filter.
If you prefer using routing to detect when to disable/enable, just use kernel.request event. You will have access to all request parameters, via $event->getRequest()->attributes->get('_controller') for example.