I have created a console command page in my bundle for cronjob.
Here is the code
class MyCommand extends Command {
protected function configure()
{
$this
->setName('cron:item_email')
->setDescription('product notification for customer that reserved');
}
protected function execute(InputInterface $input, OutputInterface $output)
{
$this->container = $this->getApplication()->getKernel()->getContainer();
$em = $this->container->get('doctrine.odm.mongodb.document_manager');
$wishlist = $em->getRepository('xxxxBundle:Wishlist')->findAll();
foreach($wishlist as $wish){
if($wish->getReservedDate()){
// $output->writeln($wish->getId());
$output->writeln($wish->getReservedDate());
}
}
}
}
Here I am retrieving mongo db date "$wish->getReservedDate()"
But I am getting the output like this
2013-07-03 13:46:42
3
Europe/Berlin
How I get the date only for ex: 2013-07-03 13:46:42
$wish->getReservedDate()->format('d/m/Y H:i:s')
Also as a side note, the ID has the date stored in it also.
Just a FYI
Related
Sorry but, I have a big problem, in a project I have to make some Cron Task where I add or edit some data in my Database and I want to get back the Doctrine error message if there is a problem.
Try Catch Test
Error I want to get back
Monolog Part 1
Monolog Part 2
As #alexcm says, you could use Monolog. Here is an example autoinjecting it in a service.
This will save the message in the log file, make sure what exception level are you saving in the logs file.
private $logger;
public function __construct(LoggerInterface $logger)
$this->logger = $logger;
}
public function add(TypeConges $entity, bool $flush = true): void
{
$this->getEntityManager()->persist($entity);
if ($flush) {
try {
$this->getEntityManager()->flush();
} catch (\Exception $e) {
$this->logger->info($e->getMessage());
print($e->getMessage());
}
}
}
API Platform version(s) affected: 2.6.8
Description
In a project which uses PostgreSQL and API-Platform, I need to filter all records by a locale string. A doctrine filter is my preferred choice to do so.
This is the filter:
class LocaleFilter extends SQLFilter
{
public const LOCALE_FILTER_NAME = 'locale_filter';
public function addFilterConstraint(ClassMetadata $targetEntity, $targetTableAlias): string
{
if (!$targetEntity->reflClass->implementsInterface(LocalizedEntityInterface::class)) {
return '';
}
return $targetTableAlias . '.locale = ' . $this->getParameter('locale');
}
}
The parameter locale will be set on each onKernelRequest event, the locale is the value of the header X-Locale:
public function onKernelRequest(RequestEvent $event): void
{
$locale = $event->getRequest()->headers->get('X-Locale');
$this->setFilterLocale($locale);
}
private function setFilterLocale(string $locale): void
{
if (!$this->entityManager->hasFilters()) {
return;
}
$localeFilter = $this->entityManager->getFilters()->getFilter(LocaleFilter::LOCALE_FILTER_NAME);
$localeFilter->setParameter('locale', $locale);
}
Now, when I send a request to a collectionOperations endpoint, such as http://example.com/products with the X-Locale header value de_DE, the filter is working and I get a response which contains only the according data in de_DE. When I send a request with locale fr_FR, I get a response with data in fr_FR.
But, when I send a request with the same X-Locale header to a itemOperations endpoint like http://example.com/products/<a-existing-id> I'm getting the error message The parameter "locale" is not set which comes from doctrine.
After investigating that issue, I can say that it works when I override the default ItemDataProvider from API-platform:
<?php
namespace App\DataProvider;
[...]
class ItemDataProvider implements ItemDataProviderInterface
{
public function __construct(
private readonly EntityManagerInterface $entityManager,
private readonly RequestStack $requestStack,
) {
}
public function getItem(string $resourceClass, $id, ?string $operationName = null, array $context = []): object
{
$locale = $this->requestStack->getMainRequest()->headers->get('X-Locale');
if ($this->entityManager->hasFilters()) {
$localeFilter = $this->entityManager->getFilters()->getFilter(LocaleFilter::LOCALE_FILTER_NAME);
$localeFilter->setParameter('locale', $locale);
}
$query = $this->entityManager->getRepository($resourceClass)
->createQueryBuilder('x')
->where('x.publicId = :pubid')
->setParameter('pubid', $id)
->getQuery();
return $query->getOneOrNullResult();
}
}
But is still required to set the filter value again in "my own" ItemDataProvider. If I delete the first 7 lines of the method getItem of the ItemDataProvider, I get the same error from above.
That doesn't make sense like that, does it? It seems like Api-Platform overrides the Doctrine filters in the default ItemDataProvider and make them useless. Howewer, I didn't found the reason for that issue.
Overriding the ItemDataProvider is a working workaround, but I don't think it's a good one, since the cause is more likely a bug and that way some features of Api-Platform are no longer present in the whole project.
I am trying to simplify my applications dependency injection by creating a base injection class.
So far most of the code works fine, except for registerForAutoconfiguration
Here is the relevant code:
abstract class AbstractTaggedPass implements CompilerPassInterface
{
protected $interfaceClass;
protected $serviceClass;
protected $tag;
protected $method;
public function process(ContainerBuilder $container)
{
// always first check if the primary service is defined
if (!$container->has($this->serviceClass)) {
return;
}
// Register classes implementing the interface with tag
$container->registerForAutoconfiguration($this->interfaceClass)->addTag($this->tag); // Does not work
$definition = $container->findDefinition($this->serviceClass);
// find all service IDs with the tag
$taggedServices = $container->findTaggedServiceIds($this->tag);
foreach ($taggedServices as $id => $tags) {
foreach ($tags as $attributes) {
$definition->addMethodCall($this->method, [new Reference($id)]);
}
}
}
}
class SubscriptionPaymentProviderPass extends AbstractTaggedPass
{
protected $interfaceClass = SubscriptionPaymentProviderInterface::class
protected $serviceClass = SubscriptionPaymentProviderPool::class;
protected $tag = 'subscription.payment_provider';
protected $method = 'addProvider';
}
class SubscriptionBundle extends Bundle
{
protected function getContainerExtensionClass()
{
return SubscriptionExtension::class;
}
public function build(ContainerBuilder $container)
{
parent::build($container);
//$container->registerForAutoconfiguration(SubscriptionPaymentProviderInterface::class)->addTag('subscription.payment_provider');
$container->addCompilerPass(new SubscriptionPaymentProviderPass());
}
}
If I move registerForAutoconfiguration line from Bundle class into the CompilerPass class, then it no longer registers Services with the correct tag.
Is it possible to use it inside a compiler pass?
Do I need to enable something to make it work?
Compiler Pass is used after service definitions are parsed (via configuration file or extensions).
I think the right place for do this, is into an Extension.
I am looking to access to a variable inside a service.
The variable is an object class.
This is the services.yml
services:
project.notification:
class: NotificationsBundle\Command\ServerCommand
// this is the class
class ServerCommand extends ContainerAwareCommand {
public $notification;
/**
* Configure a new Command Line
*/
protected function configure() {
$this->setName('Project:notification:server') ->setDescription('Start the notification server.');
}
public function getNotification()
{
return $this->notification;
}
protected function execute(InputInterface $input, OutputInterface $output) {
$this->notification = new Notification();
$server = IoServer::factory(new HttpServer(
new WsServer(
$this->notification
)
), 8081);
$server->loop->addPeriodicTimer(1, function () {
$this->notification->sendToAll('Hello');
});
$server->run();
}
}
I would like to get the variable $notification from another controller.
When I do that I got an error "non-existent object" ($notification).
I run the service by executing the following command:
php app/console Project:notification:server
It has to be the current object I can not create a new one because the list of users it is inside the object $notification.
Any ideas?
I'm trying to use doctrine entity manager in a thread. I use a static scope as suggested here .
Class A is a symfony service and doctrine entity manager is injected in service.yml
class A extends \Thread{
static $em;
public function __construct($em)
{
self::$em = $em;
}
public function run(){
self::$em->doSomething(); //here em is null
}
}
How i can use entity manager correctly from a thread?
UPDATE:
As #Mjh suggested I can't share entity manager from threads. I can have an istance of em in every threads however but this is very inefficient.
A solution could be build a container threaded class shared between threads in which I'll store the entities that return from doctrine queries. The entities obviously will be detached from entity manager but I need only a read cache shared between threads.
UPDATE2:
See my first answer
Open issue: avoid to initialize for every thread a new environment
We have built a doctrine cache shared between thread extending a Thread Safe Stackable.
Warning some parts of code are semplified for demo purpose.
class work extends \Collectable{
protected $parameters;
public static $doctrine_mongodb;
public function __construct($parameters){
$this->parameters = $parameters;
}
public function run()
{
try{
$loader = require __DIR__.'/../../../../../../vendor/autoload.php';
static::$container = unserialize($this->worker->container);
static::$doctrine_mongodb = static::$container->get('doctrine_mongodb');
...
DO WORK
$dm = static::$doctrine_mongodb->getManager();
$repo = $dm->getRepository('Bundle:Document');
$ris = $this->worker->doctrinecache->FindOneBy($repo, array('key' => $val));
...
}catch(\Exception $e){}
}
}
NB: in work class we have the parallel execution of work code and there we can safely use doctrine common cache.
It's not the same to share entity manager because document are detached but for read purpose is good. If somebody need to manage entities can use merge doctrine method.
class SWorker extends \Worker{
public $env;
public $debug;
public $mongodb_cache_engine;
public function __construct( $env, $debug, $doctrinecache, $workParams){
$this->workParams = $work;
$this->env = $env;
$this->debug = $debug;
$this->doctrinecache = $doctrinecache ;
}
public function start($options = null){
return parent::start(PTHREADS_INHERIT_NONE);
}
public function run(){
require_once __DIR__.'/../../../../../../app/bootstrap.php.cache';
require_once __DIR__.'/../../../../../../app/AppKernel.php';
$kernel = new \AppKernel($this->env, $this->debug);
$kernel->loadClassCache();
$kernel->boot();
$this->container = serialize($kernel->getContainer());
}
}
In Sworker class we prepare symfony environment for thread. Tnx to svenpelster https://github.com/krakjoe/pthreads/issues/369 for that.
class doctrinecache extends \Stackable{
public function __call($MethodName, $arguments){
$repository = array_shift($arguments);
$documentName = $repository->getDocumentName();
$hash = $this->generateHash($MethodName, $documentName, $arguments);
return $this->cacheIO($hash, $repository, $MethodName, $arguments);
}
public function cacheIO($hash, $repository, $MethodName, $arguments){
$result = isset($this["{$hash}"])? $this["{$hash}"] : NULL;
if(!$result){
$result = call_user_func_array(array($repository, $MethodName), $arguments);
$this["{$hash}"] = $result;
}
return $result;
}
}
And finally
$doctrineCache = $this->kernel->get('doctrineCacheService');
$pool = new \Pool($workerNumber, SWorker::class, [$this->kernel->getEnvironment(), $this->kernel->isDebug(), $doctrineCache ,$workParams]);
while(current($works ))
{
$pool->submit(current($works ));
next($works);
}
$pool->shutdown();
while(current($works ))
{
$arrayResults[] = current($works )->getResults();
next($works);
}