I'm trying turn off translator cache by this way:
app/config/config.yml
services:
translator.default:
class: %translator.class%
arguments: [ #service_container, #translator.selector, {}, { cache_dir: null, debug: %kernel.debug% }, #?session ]
The cached code in cache/dev/appDevDebugProjectContainer.php should be:
protected function getTranslator_DefaultService()
{
$this->services['translator.default'] = $instance = new \Symfony\Bundle\FrameworkBundle\Translation\Translator($this, new \Symfony\Component\Translation\MessageSelector(), array('translation.loader.php' => 'php', 'translation.loader.yml' => 'yml', 'translation.loader.xliff' => 'xliff'), array('cache_dir' => NULL, 'debug' => true), $this->get('session'));
... resources ...
return $instance;
}
But i get followed code:
protected function getTranslator_DefaultService()
{
return $this->services['translator.default'] = new \Symfony\Bundle\FrameworkBundle\Translation\Translator($this, new \Symfony\Component\Translation\MessageSelector(), array('translation.loader.db' => 'db', 'translation.loader.php' => 'php', 'translation.loader.yml' => 'yml', 'translation.loader.xliff' => 'xliff'), array('cache_dir' => NULL, 'debug' => true), $this->get('session'));
}
So translator resources is empty.
One way to do this is:
Edit symfony/src/Symfony/Bundle/FrameworkBundle/Translation/Translator.php and add a method:
public function setOption($option, $value)
{
$this->options[$option] = $value;
}
In your AppKernel.php override a method:
public function boot()
{
parent::boot();
$this->container->get('translator')->setOption('cache_dir', null);
}
Related
I know that segments are not supported in the current v1Beta api (https://analyticsdata.googleapis.com/v1beta/properties/{property_id}:runReport).
Is there a workaround to get segmented data from using this api?
Reference: https://developers.google.com/analytics/devguides/reporting/data/v1/rest/v1beta/properties/runReport
I was working with google analytics API GA4 using PHP hope this helps you
<?php
class analytics{
private $property_id =null;
private $client = null;
public $propertys;
public function setPropertyId($propertyid)
{
return $this->property_id = $propertyid;
}
private function getCredentials()
{
return DIR.'PATH/credentials.json');
}
private function getClient(): BetaAnalyticsDataClient
{
return $this->client= new BetaAnalyticsDataClient([
'credentials' => $this->getCredentials(),
]);
}
public function get($propertyid,$startdate,$enddate,dimensions,$metrics)
{
$response = $this->getClient()->runReport([
'property' => 'properties/' . $this->setPropertyId($propertyid),
'dateRanges' => [
new DateRange([
'start_date' => $startdate,
'end_date' => $enddate,
]),
],
'dimensions' => [new Dimension(
[
'name' => $dimensions,
]
),
],
'metrics' => [new Metric(
[
'name' => $metrics,
]
)
]
]);
foreach ($response->getRows() as $row) {
return $row->getMetricValues()[0]->getValue();
}
}
}
then you can use the get function with dates range and dimensions metrics you want
How to change the node name of XML document generated by Symfony serializer?
I generate XML with this code:
final class AdsController extends AbstractController
{
private SerializerInterface $serializer;
public function __construct(SerializerInterface $serializer)
{
$this->serializer = $serializer;
$this->normalizer = $normalizer;
}
public function __invoke(Request $request): Response
{
$ads = $this->em->findAll();
$rootNode = [
'#id' => 12345,
'#' => $ads
];
$res = $this->serializer->serialize($rootNode, 'xml', [
'xml_format_output' => true,
'xml_encoding' => 'utf-8',
'xml_root_node_name' => 'ads'
]);
return $res;
}
}
And the $res looks like this:
<?xml version="1.0" encoding="UTF-8"?>
<ads id="12345">
<item></item>
</ads>
But how to get something like this:
<?xml version="1.0" encoding="UTF-8"?>
<ads id="12345">
<ad adId="123"></ad>
</ads>
My normalizer looks like this:
class AdNormalizer implements ContextAwareNormalizerInterface
{
public function normalize($topic, string $format = null, array $context = [])
{
$data['adName'] = ...
return $data;
}
public function supportsNormalization($data, string $format = null, array $context = [])
{
return $data instanceof Ad;
}
}
I'm not sure about details in the context of vanilla Symfony, but here is how I do in Drupal using Symfony\Component\Serializer\Encoder\XmlEncoder
public function encode() {
$list = [
[
'#id' => '123',
'propertyOne' => 1,
'propertyTwo' => 2,
],
[
'#id' => '456',
'propertyOne' => 1,
'propertyTwo' => 2,
],
[
'#id' => '789',
'propertyOne' => 1,
'propertyTwo' => 2,
]
];
// The root element name defined via:
$context[XmlEncoder::ROOT_NODE_NAME] = 'ads';
$list = ['ad' => $list];
$ads = [
'#id' => 12345,
'#' => $list
];
$encoder = new \Symfony\Component\Serializer\Encoder\XmlEncoder();
return $encoder->encode($ads, $format, $context);
}
I have a formBuilder that contains a collectionType. I would like to be able to put a constraint on the email field to be sure that when the user validates, there is not the same email address several times in the form
I've :
RegistrationCollectionType.php
$builder
->add('utilisateurs', CollectionType::class, [
'entry_type' => RegistrationType::class,
'entry_options' => [
'label' => false,
'entreprise' => $entreprise,
],
'allow_add' => true,
'allow_delete' => true,
'delete_empty' => true,
'by_reference' => true,
'prototype' => true,
'label' => false,
'attr' => [
'class' => 'my-selector',
'label' => false,
],
'by_reference' => false,
])
;
With his class :
RegistrationCollection.php
class RegistrationCollection
{
private $utilisateurs = [];
public function getUtilisateurs(): ?array
{
return $this->utilisateurs;
}
public function setUtilisateurs(?array $utilisateurs): self
{
$this->utilisateurs = $utilisateurs;
return $this;
}
}
And in my RegistrationType.php which is associated with my User entity, I've :
RegistrationType.php
->add('email', EmailType::class, [
'attr' => [
'placeholder' => "Adresse email"
],
])
Now if I valid, I've :
SQLSTATE[23000]: Integrity constraint violation: 1062 Duplicata du
champ 'ahahs#mail.fr' pour la clef 'UNIQ_8D93D649E7927C74'
I kept the idea of a custom constraint, but which would not apply only to emails but to any field that we want Unique:
#App\Validator\Constraints\UniqueProperty.php
<?php
namespace App\Validator\Constraints;
use Symfony\Component\Validator\Constraint;
/**
* #Annotation
*/
class UniqueProperty extends Constraint
{
public $message = 'This collection should contain only elements with uniqe value.';
public $propertyPath;
public function validatedBy()
{
return UniquePropertyValidator::class;
}
}
and
#App\Validator\Constraints\UniquePropertyValidator.php
<?php
namespace App\Validator\Constraints;
use Symfony\Component\PropertyAccess\PropertyAccess;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintValidator;
use Symfony\Component\Validator\Exception\UnexpectedTypeException;
use Symfony\Component\Validator\Exception\UnexpectedValueException;
class UniquePropertyValidator extends ConstraintValidator
{
/**
* #var \Symfony\Component\PropertyAccess\PropertyAccessor
*/
private $propertyAccessor;
public function __construct()
{
$this->propertyAccessor = PropertyAccess::createPropertyAccessor();
}
/**
* #param mixed $value
* #param Constraint $constraint
* #throws \Exception
*/
public function validate($value, Constraint $constraint)
{
if (!$constraint instanceof UniqueProperty) {
throw new UnexpectedTypeException($constraint, UniqueProperty::class);
}
if (null === $value) {
return;
}
if (!\is_array($value) && !$value instanceof \IteratorAggregate) {
throw new UnexpectedValueException($value, 'array|IteratorAggregate');
}
if ($constraint->propertyPath === null) {
throw new \Exception('Option propertyPath can not be null');
}
$propertyValues = [];
foreach ($value as $key => $element) {
$propertyValue = $this->propertyAccessor->getValue($element, $constraint->propertyPath);
if (in_array($propertyValue, $propertyValues, true)) {
$message = sprintf("%s (%s)", $constraint->message, $propertyValue);
$this->context->buildViolation($message)
// ->atPath(sprintf('[%s]', $key))
->atPath(sprintf('[%s][%s]', $key, $constraint->propertyPath))
->addViolation();
}
$propertyValues[] = $propertyValue;
}
}
}
and
class RegistrationCollection
{
/**
* #App\UniqueProperty(
* message = "Adresse mail déjà utilisée",
* propertyPath = "email"
* )
*
*/
private $utilisateurs = [];
It works very well, except that I can't target the child field for the error. Systematically, the error will go to the parent entity, and therefore the error will be put all over it.
I tried in the validator to redirect to the fields of the child entity concerned, but nothing to do, the error continues to put everything above..
In my FormType I tried to disable error_bubbling but same thing
->add('utilisateurs', CollectionType::class, [
'entry_type' => RegistrationType::class,
'entry_options' => [
'label' => false,
'entreprise' => $entreprise,
],
'allow_add' => true,
'allow_delete' => true,
'delete_empty' => true,
'by_reference' => true,
'prototype' => true,
'label' => false,
'attr' => [
'class' => 'my-selector',
'label' => false,
],
'by_reference' => false,
'error_bubbling' => false,
])
;
You can create custom validation constraint
class UniqueEmailValidator extends ConstraintValidator
{
public function validate($value, Constraint $constraint)
{
if (!$constraint instanceof UniqueEmail) {
throw new UnexpectedTypeException($constraint, UniqueEmail::class);
}
if (!\is_array($value) && !$value instanceof \IteratorAggregate) {
throw new UnexpectedValueException($value, 'array|IteratorAggregate');
}
$emails = [];
foreach ($value as $element) {
if (\in_array($element->getEmail(), $emails, true)) {
$this->context->buildViolation($constraint->message)
->addViolation();
return;
}
$emails[] = $element->getEmail();
}
}
}
and add to your property a validation annotation
class RegistrationCollection
{
/**
* #AppAssert\UniqueEmailValidator
*/
private $utilisateurs = [];
public function getUtilisateurs(): ?array
{
return $this->utilisateurs;
}
public function setUtilisateurs(?array $utilisateurs): self
{
$this->utilisateurs = $utilisateurs;
return $this;
}
}
I'm creating a CRUD app sample where i need to connect my ZF3 app to my database
My Models Post.php and PostTable.php respectively
namespace Post\Model;
/**
*
*/
class Post
{
protected $id;
protected $title;
protected $description;
protected $category;
public function exchangeArray($data){
$this->id = $data['id'];
$this->title = $data['title'];
$this->description = $data['description'];
$this->category = $data['category'];
}
public function getId(){
return $this->id;
}
public function getTitle(){
return $this->titile;
}
public function getDescription(){
return $this->description;
}
public function getCategory(){
return $this->category;
}
}
namespace Post\Model;
use Zend\Db\TableGateway\TableGatewayInterface;
/**
*
*/
class PostTable
{
function __construct(TableGatewayInterface $tableGateway)
{
# code...
$this->tableGateway = $tableGateway;
}
public function fetchAll(){
return $tableGateway->select();
}
}
Module.config.php
namespace Post;
use Zend\Router\Http\Literal;
use Zend\Router\Http\Segment;
use Zend\ServiceManager\Factory\InvokableFactory;
return [
'router' => [
'routes' => [
'home' => [
'type' => Literal::class,
'options' => [
'route' => '/',
'defaults' => [
'controller' => Controller\IndexController::class,
'action' => 'index',
],
],
],
'application' => [
'type' => Segment::class,
'options' => [
'route' => '/post[/:action]',
'defaults' => [
'controller' => Controller\IndexController::class,
'action' => 'index',
],
],
],
],
],
'controllers' => [
'factories' => [
Controller\IndexController::class => InvokableFactory::class,
],
],
'view_manager' => [
'display_not_found_reason' => true,
'display_exceptions' => true,
'doctype' => 'HTML5',
'not_found_template' => 'error/404',
'exception_template' => 'error/index',
'template_map' => [
'layout/layout' => __DIR__ . '/../view/layout/layout.phtml',
'application/index/index' => __DIR__ . '/../view/application/index/index.phtml',
'error/404' => __DIR__ . '/../view/error/404.phtml',
'error/index' => __DIR__ . '/../view/error/index.phtml',
],
'template_path_stack' => [
__DIR__ . '/../view',
],
],
];
My Module.php
namespace Post;
use Zend\Db\AdapterInterface;
use Zend\Db\TableGateway;
use Zend\Db\ResultSet\ResultSet;
use Zend\ModuleManager\Feature\ConfigProviderInterface;
class Module implements ConfigProviderInterface
{
const VERSION = '3.0.3-dev';
public function getConfig()
{
return include __DIR__ . '/../config/module.config.php';
}
public function getServiceConfig(){
return [
'factories' => [
Model\PostTable::class => function($container){
$tableGateway = $container->get(Model\PostTableGateway::class);
return new Model\PostTable($tableGateway);
},
Model\PostTableGateway::class => function($container){
$adapter = $container->get(AdapterInterface::class);
$resultSetPrototype = new ResultSet();
$resultSetPrototype->setArrayObjectPrototype(new Model\Post);
return new tableGateway('post',$adapter,null,$resultSetPrototype);
}
],
];
}
public function getControllerConfig(){
return [
'factories' => [
controller\IndexController::class => function($container){
return new Controller\IndexController($controller->get(Model\PostTable::class));
}
]
];
}
}
IndexController.php
namespace Post\Controller;
use Zend\Mvc\Controller\AbstractActionController;
use Zend\View\Model\ViewModel;
class IndexController extends AbstractActionController
{
public function __construct($table){
$this->table = $table;
}
public function indexAction()
{
return new ViewModel();
}
}
my ArgumentCountError exception
Too few arguments to function Post\Controller\IndexController::__construct(), 0 passed in F:\Path\to\Project\vendor\zendframework\zend-servicemanager\src\Factory\InvokableFactory.php on line 30 and exactly 1 expected
F:\Path\to\Project\module\Post\src\Controller\IndexController.php:15
The line 15
public function __construct($table){
$this->table = $table;
}
I expect to get a Result Set from the database. But the Constructor outputs an Argument Count Error exception
There are two errors in your code.
First one is inside module.config.php, under the key controllers you must remove the old IndexController definition:
'router' => [
...
],
'controllers' => [
'factories' => [
// This one is obsolete, and you must remove it.
// Controller\IndexController::class => InvokableFactory::class,
],
],
'view_manager' => [
...
],
Since you are declaring controllers inside Module.php class, controllers key in module.config.php is not necessary anymore, and you could remove it:
'router' => [
...
],
'view_manager' => [
...
],
Second one: there is a (double) mistake in IndexController factory (in Module.php).
The first is a wrongly typed namespace, controller instead of Controller.
The second one is that you use $controller instead of $container to retrieve the PostTable instance.
public function getControllerConfig() {
return [
'factories' => [
Controller\IndexController::class => function($container) {
return new Controller\IndexController($container->get(Model\PostTable::class));
}
]
];
}
I have a REST API and have an Entity Userwith field called Avatar, in DB I save name XXXX.jpg but when I return I want to add a url in this field Avatar, for example www.mylink.com/XXXX.jpg.
I'm trying with a service implements SubscribingHandlerInterfacebut I don't know how I can use it.
I have this method in this service:
class UrlManager implements SubscribingHandlerInterface
{
public static function getSubscribingMethods()
{
return array(
array(
'direction' => GraphNavigator::DIRECTION_SERIALIZATION,
'format' => 'json',
'type' => 'AppBundle/Entity/User',
'method' => 'serializeUrlAvatar',
),
);
}
public function serializeUrlAvatar(User $user)
{
$url = 'www.mylink.com';
return array(
"avatar" => $url . $user->getAvatar()
);
}
}
but how can I call this service to modify url when I serialize.
Now I do this:
$_format = 'json';
$json = $this->get('jms_serializer')->serialize($user, $_format);
return new Response($json, 200, ['Content-Type' => 'application/' . $_format]);
In service.yml:
app.url_converter_service:
class: AppBundle\Service\UrlManager
tags:
- { name: jms_serializer.subscribing_handler }
Update
In my controller I call this function like this:
$result = $this->get('app.url_converter_service')->serializeUrlAvatar($user);
$json = $this->get('jms_serializer')->serialize($result, $_format);
return new Response($json, 200, ['Content-Type' => 'application/' . $_format]);
So my question is, exists a way to remove the first line and serialize correctly (add the url) when I serialize?
Have you registered your service like this?
# app/config/services.yml
avatar_url_handler:
class: YourBundle\Serializer\Handler\AvatarUrlHandler
tags:
- { name: jms_serializer.subscribing_handler }
I found a solution. I create a service which implements EventSubscriberInterface like this:
class UserSerializeHandler implements EventSubscriberInterface
{
private $user_uploads;
public function __construct($user_uploads){
$this->user_uploads = $user_uploads;
}
public static function getSubscribedEvents()
{
return array(
array(
'event' => 'serializer.pre_serialize',
'class' => User::class,
'method' => 'onPreSerializeUser'
));
}
public function onPreSerializeUser(PreSerializeEvent $event)
{
/** #var User $user */
$user = $event->getObject();
$avatar = $user->getAvatar();
$user->setAvatar($this->user_uploads . "/" . $avatar);
}
}
In service.yml:
app.serializer_user_service:
class: AppBundle\Service\UserSerializeHandler
arguments: ['%user_uploads%']
tags:
- { name: jms_serializer.event_subscriber }
I have user_uploads in parameters.yml like this:
user_uploads: 'https://myUrl.com'
And in any Controller that I serialize a User, I add the url in the Avatar paramter.
$json = $this->get('jms_serializer')->serialize($user, $_format);
return new Response($json, 200, ['Content-Type' => 'application/' . $_format]);