SymfonyCommonBundle
Useful features for Symfony, Doctrine, Twig etc., (*1)
Debug
Easy way to start debug with RunnerCommand
If you are using PhpStorm, then an easy way to use this feature is to create an external tool (File-> Settings-> Tools-> External Tools) with parameters:
- Name: Runner
- Program: /usr/bin/php
- Arguments: -d xdebug.remote_autostart=1 -d xdebug.remote_enable=1 bin/console symfony-common:runner $FilePath$ $LineNumber$
- Working Directory: $ProjectFileDir$, (*2)
After that, select Tools -> External Tools -> Runner - the command will try to execute a function or method from where the cursor is currently located.
At this point, execution will not automatically stop at the specified location - you must manually set a breakpoint., (*3)
Doctrine
Cosmologist\Bundle\SymfonyCommonBundle\Doctrine\ExtraConnection is Doctrine DBAL-connection wrapper that add useful features and methods to DBAL., (*4)
Activation
Add the wrapper_class parameter to the Doctrine DBAL connection configuration in config.yml to use:, (*5)
default:
driver: pdo_mysql
dbname: ~
user: ~
password: ~
host: ~
wrapper_class: \Cosmologist\Bundle\SymfonyCommonBundle\Doctrine\ExtraConnection
Additional DBAL-events
postBeginTransaction, postCommit, postRollback, (*6)
Helper methods
Connection::fetchAllIndexed prepares and executes an SQL query and returns the result as an associative array, each row in the result set array is indexed by the value of the first column., (*7)
$connection->fetchAllIndexed('SELECT id, name FROM users');
// 1 => [id: 1, name: Ivan]
// 7 => [id: 7, name: Vasiliy]
// ...
Doctrine Utils
Get Doctrine utils, (*8)
$utils = $container->get(Cosmologist\Bundle\SymfonyCommonBundle\Doctrine\DoctrineUtils::class);
// or
$utils = $container->get('symfony_common.doctrine.utils');
Get real class of Doctrine entity (resolve entity proxy class)
Supports Doctrine < 3.x and Doctrine > 3.x, (*9)
$entity = $entityManager->find(App\Entity\Foo::class, $identifier);
DoctrineUtils::getRealClass($entity); \\ "App\Entity\Foo"
DoctrineUtils::getRealClass(App\Entity\Foo::class); \\ "App\Entity\Foo"
Simple way to get doctrine entity metadata, (*10)
$utils->getClassMetadata($entity);
$utils->getClassMetadata(Entity::class);
Get the target class name of the given association path (ie "contact.user") recursively, (*11)
$doctrineUtils->getAssociationTargetClassRecursive('AppBundle/Entity/Company', 'contact.user'); // 'AppBundle/Entity/User'
Get entity identifier field name (does not support multiple identifiers - throws DoctrineUtilsException), (*12)
$utils->getEntitySingleIdentifierField($entity);
$utils->getEntitySingleIdentifierField(Entity::class);
Get entity identifier value (does not support multiple identifiers - throws DoctrineUtilsException), (*13)
$utils->getEntitySingleIdentifierValue($entity);
Determine if the object or FQCN is a Doctrine entity (under Doctrine control) or not, (*14)
$utils->isEntity($entity);
Get the readable alias for the doctrine entity, (*15)
$this->getEntityAlias(FooBundle\Entity\Bar\Baz::class); // 'foo.bar.baz'
$this->decodeEntityAlias('foo.bar.baz'); // 'FooBundle\Entity\Bar\Baz'
Perform recursively join operation of the given association path (ie "contact.user.type"), (*16)
$qb = $entityManager->getRepository(Company::class)->createQueryBuilder('company');
# Recursive joins
DoctrineUtils::joinRecursive($qb, 'contact.user.type');
// equivalent to
$qb
->join('company.contact', 'contact')
->join('contact.user', 'user')
->join('user.type', 'type');
Attention: method doesn't care about alias uniqueness, (*17)
Add a join to the query once, (*18)
// Adds join and returns an alias of added join
DoctrineUtils::joinOnce($qb, 'contact.user', 'u1'); // "u1"
// If a join with specified parameters exists then only returns an alias of existed join
DoctrineUtils::joinOnce($qb, 'contact.user', 'u2'); // "u1"
````
Merge multiple `Doctrine\Common\Collections\Criteria` into a one `Doctrine\Common\Collections\Criteria`
```php
$resultCriteria = DoctrineUtils::mergeCriteria($firstCriteria, $secondCriteria);
Routing
Forwards to another URI.
Like Symfony\Bundle\FrameworkBundle\Controller\Controller::forward, but using URI., (*19)
$utils = $container->get('symfony_common.routing.utils');
$utils->forwardToUri('/products/programmers-t-shirts');
// or
$utils->forwardToUri('https://myshop.com/products/programmers-t-shirts');
Security
Command for interactive setup ACLs and ACEs
```shell script
bin/console symfony-common:acl:set, (*20)
### ROLE_SUPER_USER
*Cosmologist\Bundle\SymfonyCommonBundle\Security\Voter\SuperUserRoleVoter* adds a special role "ROLE_SUPER_USER" which effectively bypasses any, and all security checks.
### The service Crypto
The service `Crypto` provides functions for the _simple symmetric encryption_.
The _framework.secret_ used as a key to encryption.
```php
$crypto = $container->get(Cosmologist\Bundle\SymfonyCommonBundle\Security\Crypto::class);
$crypto->decrypt($crypto->encrypt('The sensitive string')); // 'The sensitive string'
Dependency Injection
Convenient way to get a Reference to a Doctrine DBAL connection
DependencyInjectionUtils::getDoctrineDbalConnectionReference('default'); // doctrine.dbal.default_connection
Convenient way to get a Reference to a Doctrine EntityManager
DependencyInjectionUtils::getDoctrineOrmEntityManagerReference('default'); // doctrine.orm.default_entity_manager
Store key as attribute in configuration
Useful for:
- to simplify your configuration
- to avoid problem of losing the key when you merge config across files (Symfony Issue #29817), (*21)
Usage:, (*22)
# AppBundle\DependencyInjection\Configuration.php
...
use Cosmologist\Bundle\SymfonyCommonBundle\DependencyInjection\DependencyInjectionUtils;
...
->arrayNode('events')
->beforeNormalization()
->always(ConfigurationUtils::useKeyAsAttribute('server'))
->end()
->prototype('array')
...
Config like this:, (*23)
something:
servers:
serverA:
username: userA
password: passwordA
serverB:
username: userB
password: passwordB
comes out like:, (*24)
servers [
serverA => [username: userA, password: passwordA, server: serverA],
serverB => [username: userB, password: passwordB, server: serverB],
]
ServiceBridge
A convenient way to dynamically access symfony services., (*25)
Call Symfony services over HTTP
Include routing.yml
# app/config/routing.yml
admin.service:
resource: "@SymfonyCommonBundle/Resources/config/routing.yml"
prefix: /admin
Send POST-request
URL example:
yourdomain.com/bridge/mybundle.foo/bar
or
yourdomain.com/bridge/MyBundle\Foo/bar
, (*26)
-
/bridge is a ServiceBridge route suffix
-
mybundle.foo (or MyBundle\Foo) is a service name
-
process is service method name
Method arguments must be passed as POST parameters.
ServiceBridge automatically fetches a Doctrine entity if the method expects an argument of the entity (the hint type of the argument)., (*27)
The method arguments should be passed as POST-parameters.
ServiceBridge fetch entity from Doctrine automatically, by the identifier from request, if method expects entity argument (argument type-hint)., (*28)
Caution: Use security access_control option to restrict access to the service controller., (*29)
Return types to response types map
- array|object -> json
- binary string -> response with content-disposition=attachment and binary content-type
- another scalar -> simple response
Static access to the service container from anywhere
use Cosmologist\Bundle\SymfonyCommonBundle\DependencyInjection\ContainerStatic;
ContainerStatic::getContainer();
ContainerStatic::get('serivice_id');
ContainerStatic::getParameter('parameter_id');
Twig
{% include '@SymfonyCommon/pagination.html.twig' with { page: current_page, count: items_total, limit: items_per_page } %}
{# Parameters:
* page (int) : The current page you are in
* limit (int): Number of records to display per page
* count (int): Total count of records
* currentFilters (array)(optional) : associative array that contains route-arguments #}
Inject any callable to the Twig
# app/config/config.yml
symfony_common:
twig:
php_extension:
filters:
- strip_tags # register php "strip_tags" function as twig "strip_tags" filter
-
foo_bar: # register static method MyApp\Foo::bar as twig filter "foo_bar"
- MyApp\Foo
- bar
functions:
- time # register php "time" function as twig "time" function
See also: umpirsky/twig-php-function, (*30)
Monolog
Monolog activation strategy for Symfony 3.x to skip the 404 HttpException records.
Monolog NotFoundActivationStrategy (activation_strategy, excluded_404s and excluded_http_codes options) does not work in Symfony 3.0 as currently monolog-bundle injects a reference to request from the service container into the NotFoundActivationStrategy., (*31)
TODO
- Other HTTP-codes support
- Configure default actionLevel value via Configuration
Usage
main:
type: fingers_crossed
handler: grouped
activation_strategy: symfony_common.monolog.fingers_crossed.ignore_http_not_found_activation_strategy
BrowserKit
Add the specified HTTP-header to the prepared BrowserKit request, (*32)
use Cosmologist\Bundle\SymfonyCommonBundle\BrowserKit\BrowserKitUtils;
/** @var \Symfony\Component\BrowserKit\Client $cient */
BrowserKitUtils::addHeader($client, 'header-name', 'header-value');
````
## Dump configuration files for external related applications
Useful when you want to deduplicate application parameters (like db-connections, paths etc) and store related external applications configurations (backup-systems, crontab etc) inside the project.
### Example
Configuration for abstract backup-system
Configuration file template:
```yaml
# app/config/external/dist/backup.yml.twig
backup:
mysql:
{{ name }}
{{ user }}
{{ password }}
compress: yes
sync-to: amazon-s3
Define parameters for dist:, (*33)
# app/config/config.yml
symfony_common:
external_config:
superbackup:
name: '%doctrine.connection.default.database_name%'
user: '%doctrine.connection.default.database_user%'
password: '%doctrine.connection.default.database_password%'
Run dumper:, (*34)
php app/console symfony-common:external-config:dump superbackup backup.yml.twig --env=prod --no-debug
And config should be dumped to app/cache/prod/external_config/backup.yml (without .twig extension), (*35)