Symfony API-Platform serializer maxDepth does not work - symfony

The Symfony API-Platform app I am working with has many entities with relations with self and they are recursively requesting the relations and can then exceed the memory and crash.
I did find this question here on SO, but there's no conclusive solution.
Attempting to limit the depth of the recurrence, I did the follow the documentation, as follows:
/config/packages/framework
framework:
serializer:
default_context:
enable_max_depth: true
I am not sure if the above is being actually applied, as it seems to accept anything under default_context. But it does show correctly when I run php bin/console debug:config framework.
The documentation above states that enable_max_depth needs to be set to true, but it is unclear on where/how to change that.
/src/Entity/SectorHierarchy
use Symfony\Component\Serializer\Annotation\MaxDepth;
#[
ORM\ManyToOne(targetEntity: self::class),
Groups(['sectorHierarchy:post', 'sectorHierarchy:get', 'sectorHierarchy:patch']),
MaxDepth(1)
]
private ?self $parent = null;

The problem was where to set the enable_max_depth to true.
So the code above I placed on /config/packages/framework is not applicable.
Instead the following should be added to the normalization_context:
'get' => [
'normalization_context' => [
'groups' => ['sectorHierarchy:get'],
'enable_max_depth' => true,
]
]

Related

my entities are no longer exposed on API PLATFORM after upgrading

I just upgraded API platform to version 3.0.
After the few classical modifications linked to the version upgrade, and despite the use of: php bin/console api:upgrade-resource
I notice that my entities are not exposed anymore when I go to the API documentation and if I try to access an endpoint, I get a route error:
No route found for "GET https://127.0.0.1:9000/api/XXX
I replaced all the ApiResource uses in my entities and rewrote my annotations.
example of an entity:
<?php
namespace App\Entity\Test;
use ApiPlatform\Metadata\ApiResource;
#[ApiResource(
collectionOperations: [
'get',
'post' => [
'denormalization_context' => ['groups' => ['create:xxx']]
]
],
itemOperations: [
'get' => [
'normalization_context' => ['groups' => ['read:fully:xxx']]
],
'put' => [
'denormalization_context' => ['groups' => ['update:xxx']]
],
'delete'
],
normalizationContext: ['groups' => ['read:xxx']]
)]
class Departement
{
....
}
Thanks in advance!
ok so, i update a little entity manually and now she is exposed !
<?php
namespace App\Entity\Agorha;
//use ApiPlatform\Core\Annotation\ApiResource;
use ApiPlatform\Metadata\ApiResource;
use ApiPlatform\Metadata\Get;
use ApiPlatform\Metadata\GetCollection;
use ApiPlatform\Metadata\Post;
use App\Entity\ChoixEcole;
use App\Repository\Agorha\EcoleRepository;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
#[ORM\Entity(repositoryClass: EcoleRepository::class)]
#[ORM\Table(name: "agorha_ecole")]
#[ORM\HasLifecycleCallbacks()]
#[ApiResource(operations: [
new Get(),
new GetCollection()
])]
#[
UniqueEntity('code')
]
class Ecole
{
#[ORM\Id()]
#[ORM\GeneratedValue()]
#[ORM\Column(type: "integer")]
private $id;
i didn't see the results of the upgrade command, which ended up being an error and therefore did nothing.
in fact, it does not seem to exist
Command "api:upgrade-resource" is not defined.
anyone would know why ?
It looks like your entities are still in the <= v2.6 format despite your call to the api:upgrade-resource command.
See the migration documentation: instead of
'get', 'post', etc. you should use new Metadata classes Get(), Post(), etc.
Are you sure that the migration command returned no error?
In my case (migration from 2.6 to 3.0) the migration command was not available for unknwon reason (not found from the console).
Try migrating manually one entity to the new format and watch your openApi documentation to see if your endpoints are back.
Edit: Why didn't api:upgrade-resource work ?
As far as I understand, the migration command is provided in v2.7 to prepare migration to 3.0 but has been dropped from v3.0. So the proper way to migrate according to the doc is:
migrate to 2.7
call api:upgrade-resource and check all is working
THEN migrate to 3.0
like #MendelYev says i should run the api upgrade command before upgrade.
now i ve to upgrade manually my entities using PHP attributes and new Metadata classes from Doctrine

OroPlatform: add custom field on core Entity

I'm currently working on an OroPlatform project and I need to add a custom field on the BusinessUnit core entity.
I have read the Oro documentation section about the way to extend core entities : https://doc.oroinc.com/backend/entities/extend-entities/#id1
<?php
namespace MyBundle\Bundle\AppBundle\Migrations\Schema\v1_0;
use Doctrine\DBAL\Schema\Schema;
use Oro\Bundle\EntityExtendBundle\EntityConfig\ExtendScope;
use Oro\Bundle\MigrationBundle\Migration\Migration;
use Oro\Bundle\MigrationBundle\Migration\QueryBag;
class AddColumnsToBusinessUnit implements Migration
{
public function up(Schema $schema, QueryBag $queries)
{
$table = $schema->getTable('oro_business_unit');
$table->addColumn('siret', 'string', [
'oro_options' => [
'extend' => ['owner' => ExtendScope::OWNER_CUSTOM],
'entity' => ['label' => 'siret'],
],
]);
}
}
When I run the command symfony console oro:migration:load --force, it works and the migration is applied to my database.
Now, I want a required field. I have seen the instruction 'notnull' => true to setup a non nullable field on the database.
Everything works well, but my field hasn't any JavaScript validation on the organization/business_unit/create route. Any ideas ?
You can validate the new field by extending the validation metadata that is already defined for the core entity you are extending.
To do this, please follow the official Symfony documentation and use the YML format:
https://symfony.com/doc/4.4/validation.html#constraint-configuration
The constraint that you can use for the field is "not blank."
Here is an example:
# src/<YourBundlePath>/Resources/config/validation.yml
Oro\Bundle\OrganizationBundle\Entity\BusinessUnit:
properties:
siret:
- NotBlank: ~

Symfony validator translations

I just started using symfony validator and i really like it except translation part, currently it uses my own translator lib, but i found validator.LOCALE.xlf files where are translations for almost all languages stored, and i can't figure out how to use them.
My current validator registering code is
$container->register('validator', \Symfony\Component\Validator\Validator\ValidatorInterface::class)
->setFactory(
[
new Reference('validator.builder'),
'getValidator'
]
);
$container->register('validator.builder', \Symfony\Component\Validator\ValidatorBuilderInterface::class)
->setFactory(
[
\Symfony\Component\Validator\Validation::class,
'createValidatorBuilder'
]
)
->addMethodCall(
'setTranslator',
[
new Reference('translator') // Symfony translatorInterface
]
)
->addMethodCall(
'setTranslationDomain',
[
'messages'
]
);
It looks like i checked already whole validator structure, like RecursiveValidator, ContextualValidator, Contexts and etc, but just somewhere missing one single param, on another hand ConstraintViolationBuilder just simply takes passed translator and trying to translate constraint message through it, no attempts to use any xlf files.
Just force search through all validator library files gave no result too.
Symfony guilde didn't helped too, because it offers to use default error sentences as a translation key, and use this "keys" in your own translations files, but why copy already translated sentences to your own file, and also create a mess with keys pattern (for example i use snake case) when there is already structured files exists (i am talking about .xlf)?
Solution is to add xlf file loader to your translator, and pass it .xlf translations as a resource.
Something like that
$container->register('translator.xlf_file_loader', \Symfony\Component\Translation\Loader\XliffFileLoader::class);
$container->register('translator.php_file_loader', \Symfony\Component\Translation\Loader\PhpFileLoader::class);
$container->register('translator', \Project\Framework\Translation\Translator::class)
->addArgument(
new Reference('service_container')
)
->addMethodCall(
'addLoader',
[
'php',
new Reference('translator.php_file_loader')
]
)
->addMethodCall(
'addLoader',
[
'xlf',
new Reference('translator.xlf_file_loader')
]
)
->addMethodCall('addResource', ['php', __DIR__ . '/../translation/lt.php', 'lt'])
->addMethodCall('addResource', ['php', __DIR__ . '/../translation/en.php', 'en'])
->addMethodCall('addResource', ['php', __DIR__ . '/../translation/ru.php', 'ru'])
->addMethodCall('addResource', ['xlf', __DIR__ . '/../../vendor/symfony/validator/Resources/translations/validators.lt.xlf', 'lt'])
->addMethodCall(
'setFallbackLocales',
[
['lt']
]
);
Have you simply tried to either
change the locale in the app configuration:
# config/packages/translation.yaml
framework:
default_locale: 'en'
translator:
fallbacks: ['en']
Documentation
change the locale based on (user) input:
public function onKernelRequest(GetResponseEvent $event)
{
$request = $event->getRequest();
// some logic to determine the $locale
$request->setLocale($locale);
}
Documentation

Symfony2 - Dynamic Doctrine Database Connections at Runtime

I am looking for a good solution to on-the-fly connection of databases within Symfony utilizing Doctrine for entity management.
The scenario I have is that all inbound users to our service will be visiting *.website.com addresses, like client1.website.com.
We would like to use one Doctrine entity for the Client table to then look up their database credentials based on the URL of their account on the fly.
So far I have found the following topics here on stackoverflow that discuss dynamically changing the database credentials--but no clear workable solutions.
I'd like to propose collaborating to put together a working solution, and I'll put together a blog/tutorial post for other folks looking to modify database connection parameters within Symfony.
Here are some related posts:
Dynamic database connection symfony2
Symfony2, Dynamic DB Connection/Early override of Doctrine Service
Thanks!
If $em is existing entity manager and you want to reuse it's configuration, you can use this:
$conn = array(
'driver' => 'pdo_mysql',
'user' => 'root',
'password' => '',
'dbname' => 'foo'
);
$new = \Doctrine\ORM\EntityManager::create(
$conn,
$em->getConfiguration(),
$em->getEventManager()
);
I needed to do something similar - runtime discovery of an available database server. I did it by overriding the doctrine.dbal.connection_factory.class parameter and substituting my own derivation of the Doctrine bundle's ConnectionFactory class
My services.yml provides the parameter, pointing at my custom class
parameters:
doctrine.dbal.connection_factory.class: Path\To\Class\CustomConnectionFactory
Then fill in your discovery logic in Path\To\Class\CustomConnectionFactory.php
<?php
namespace Path\To\Class;
use Doctrine\Bundle\DoctrineBundle\ConnectionFactory;
use Doctrine\Common\EventManager;
use Doctrine\DBAL\Configuration;
class CustomConnectionFactory extends ConnectionFactory
{
public function createConnection(array $params, Configuration $config = null, EventManager $eventManager = null, array $mappingTypes = array())
{
// Discover and override $params array here.
// A real-world example might obtain them from zookeeper,
// consul or etcd for example. You'll probably want to cache
// anything you obtain from such a service too.
$params['driver'] = 'pdo_mysql';
$params['host'] = '10.1.2.3';
$params['port'] = 3306;
$params['dbname'] = 'foo';
$params['user'] = 'myuser';
$params['password'] = 'mypass';
//continue with regular connection creation using new params
return parent::createConnection($params, $config, $eventManager,$mappingTypes);
}
}
Note also that Symfony 3.2 features the ability to use environment variables in container configurations, and to use their values on-demand (rather than fixing them when the container is compiled). See the blog announcement for more details.

Limit languages in cms

I use silverstripe 3.1
I would like to limit the languages (To only German and English) which are available in the drop down in the CMS. Therefore I put
the following code in my mysite/_config.php
i18n::set_locale('de_DE');
$allowed_locales = array(
'de_DE' => array('Deutsch', 'Deutsch'),
'en_EN' => array('English', 'English')
);
i18n::$common_locales = $allowed_locales;
Afer a flush=1 i get the following error:
Fatal error: Cannot access private property i18n::$common_locales in ... _config.php
Any ideas what goes wrong?
Thank you
as of 3.1 most of the static php variables are private. this means you can no longer access those.
the reason for this api change is that they are now cached by the config layer (this is also why you have to ?flush=1 now after changing private statics in classes like for example with private static $db)
if you want to update something in the config layer, you can do this with:
Config::inst()->update('CLASS', 'FIELD', $value);
you could use use the config update to overwrite the common locales (class would be 'i18n', and field would be 'common_locales'):
Config::inst()->update('i18n', 'common_locales', $array);
Note: if you want to completely overwrite an existing configuration, you have to do a remove() first.
Config::inst()->remove('i18n', 'common_locales');
Config::inst()->update('i18n', 'common_locales', $array);
however, if you are using the translatable module and you want to limit the number of translatable languages, there is a much better way already built in:
// in your _config.php
i18n::set_locale('en_US');
Translatable::set_allowed_locales(array(
'de_DE',
'en_US',
));
Config it through YAML:
i18n:
common_locales:
nl_BE:
name: Dutch (Belgium)
native: Nederlands
fr_BE:
name: French (Belgium)
native: Francais

Resources