I'm looking to retrieve information from an entity by twig by passing the id as a parameter. But I block on the function in my entity:
My entity (function need call):
public function getNameFournisseur($id)
{
???
}
My twig:
{{ staticFournisseur.getNameFournisseur(idFournisseur) }}
My controller:
/**
* #Route("/new", name="new_invoice", methods={"GET","POST"})
*/
public function new(Request $request, SessionInterface $session, ArticlesRepository $articlesRepository, FournisseursRepository $fournisseursRepository): Response
{
$invoice = new Invoice();
$form = $this->createForm(InvoiceType::class, $invoice);
$form->handleRequest($request);
$articles = $session->get('articleInvoice', []);
$articleData = [];
foreach ($articles as $k => $article) {
$articleData [] = [
'articleInvoice' => $articlesRepository->find($k),
'quantityInvoice' => $article
];
}
$total = 0;
foreach ($articleData as $totalArticle) {
$totalArticles = $totalArticle['articleInvoice']->getPrice() * $totalArticle['quantityInvoice'];
$total += $totalArticles;
}
$session->set('totalHt', $total);
$totalAllArticles = $session->get('totalHt');
$tauxDiscount = $session->get('discountTaux');
if (!$tauxDiscount) {
$totalWithDiscount = $total;
} else {
$totalWithDiscount = $totalAllArticles - ($totalAllArticles * $tauxDiscount) / 100;
}
if ($form->isSubmitted()) {
//$session->remove('idFournisseur');
$id_fournisseur = (int)$request->request->get('order')['fournisseur_id'];
$fournisseur = $fournisseursRepository->findOneBy(['id' => $id_fournisseur]);
$entityManager = $this->getDoctrine()->getManager();
$entityManager->persist($invoice);
$entityManager->flush();
return $this->redirectToRoute('invoices');
}
return $this->render('admin/invoices/new.html.twig', [
'staticFournisseur' => new Fournisseur(),
'idFournisseur' => $session->get('idFournisseur'),
'discountTaux' => $tauxDiscount,
'totalHt' => $totalWithDiscount,
'art' => $articleData,
'form' => $form->createView()
]);
}
I therefore seek to recover the name of the supplier thanks to the id that I pass as a parameter in my twig.
getNameFournisseur($id) is a function that retrieves data from the database, not from a single (already loaded) entity; in other words, it should be in FournisseursRepository instead
Also, if you're using Doctrine, you usually want to load the full entity instead of just a field
'staticFournisseur' => $fournisseursRepository->findOneById($session->get('idFournisseur'));
and in twig:
{{ staticFournisseur.getName() }}
or even
{{ staticFournisseur.name }}
I'm having a problem recovering my entities, the entities are in the AppBundle / Entity folder, but symfony can not find it ...
Here is the error: Class 'Product' does not exist
Here is the function myManager () present in a controller
public function myManager(){
$isDevMode = true;
$config = Setup::createAnnotationMetadataConfiguration(array(__DIR__."/AppBundle/Entity"), $isDevMode);
// database configuration parameters
$conn = array(
'dbname' => 'teste',
'user' => 'root',
'password' => '',
'host' => '127.0.0.1',
'driver' => 'pdo_mysql',
);
$entityManager = EntityManager::create($conn, $config);
return $entityManager;
}
the function testAction () that calls the manager and tries to load the Product entity
public function testAction(){
$em = $this->myManager()->getRepository('Product');
return $this->render('toto.html.twig');
}
link of documentation : Doctrine
You need to use the correct notation to make a reference to your entity:
$manager->getRepository('MyBundleName:Product')
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]);
I am using FOSElasticaBundle with symfony2 and doctrine 2.
I have trouble understanding how to retrieve actual doctrine objets from a search result. I am under the impression that it is the default behaviour but I get this kind of result :
object(Elastica\Result)[1239]
protected '_hit' =>
array (size=5)
'_index' => string 'foodmeup' (length=8)
'_type' => string 'recipes' (length=7)
'_id' => string '2' (length=1)
'_score' => float 2.2963967
'_source' =>
array (size=5)
'name' => string 'Bavaroise vanille' (length=17)
'nickName' => string 'Bavaroise vanille' (length=17)
'content' => null
'userRecipes' =>
array (size=1)
...
'tags' =>
array (size=0)
Here is my FOSElasticaBundle configuration:
#Elastic Search
fos_elastica:
default_manager: orm
clients:
default: { host: localhost, port: 9200 }
indexes:
search:
client: default
index_name: foodmeup
types:
recipes:
mappings:
name: { type: string, boost: 5}
nickName: { type: string }
content: { type: string }
userRecipes:
type: "nested"
properties:
name: { type: string }
content: { type: string }
tags:
type: "nested"
boost: 5
properties:
name: { type: string }
persistence:
driver: orm
model: AppBundle\Entity\FoodAnalytics\Recipe
repository: AppBundle\Repository\FoodAnalytics\RecipeRepository
provider: ~
finder: ~
listener: ~ # by default, listens to "insert", "update" and "delete"
And the code in my controller :
public function searchAction(Request $request)
{
$search = $request->query->get('search');
$finder = $this->get('fos_elastica.index.search.recipes');
$results = $finder->search($search)->getResults();
return array(
'search' => $search,
'results' => $results
);
}
I understood I could use a custom repository method to get the objects, but before I reach that point, what is the default way to get objects ? (Here I want a Recipe Object, an instance of my model).
Thanks a lot !
Got it!
I called the wrong service. The correct controller code to retrieve directly object instances is:
public function searchAction(Request $request)
{
$search = $request->query->get('search');
$finder = $this->get('fos_elastica.finder.search.recipes');
$results = $finder->find($search);
return array(
'search' => $search,
'results' => $results
);
}
I created a parameter in my parameters.yml file:
parameters:
category:
var: test
How can I access this parameter in my config.yml? For example, I want to pass this parameter to all my twig files as a global twig variable:
twig:
globals:
my_var: %category.var% # throws ParameterNotFoundException
In all the symfony config files I've seen, the entries under 'parameters:' have always been fully qualified. I don't fully understand why this is but it may help you to write the entries in your parameters.yml like this:
category1.var1: xxx
category1.var2: yyy
category1.var3. zzz
category2.subcategory1.var1: 5
category2.subcategory1.var2: 10
category2.subcategory2.var1: foo
category2.subcategory2.var2: bar
... and so on.
EDIT
I tried pasting the nested parameter from the question into parameters.local.yml in one of my projects and ran a trivial unit test to retrieve that and a fully qualified parameter from the container e.g.
$testUserEmail = $container->getParameter('test.user.email');
$this->assertEquals('dahlia.flower#randomtest.com', $testUserEmail);
$testParam = $container->getParameter('category.var');
$this->assertEquals('test', $testParam);
The fully qualified parameter was fine, attempting to get the nested parameter resulted in an InvalidArgumentException: The parameter category.var must be defined. I don't think parameters can be defined with nesting.
$this->container->getParameter('category')['var']
I have tested that on symfony 2.8 and it worked for me.
Or you can write simple ParameterBag which can implement nested parameters dynamically (without config values duplicity):
<?php
namespace Your\Namespace;
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag;
use Symfony\Component\DependencyInjection\Exception\ParameterNotFoundException;
class ParameterBagNested extends ParameterBag
{
/**
* wire $this->set() logic into add() too
*
* #param array $parameters
*/
public function add( array $parameters )
{
foreach ( $parameters as $name => $value ) {
$this->set( $name, $value );
}
}
/**
* sets all levels of nested array parameters with dot notation
* - loggly[host: loggly.com] will be translated this way:
* - loggly: [host: loggly.com] - standard array parameter will be left as is
* - loggly.host: loggly.com - nested variables ar translated so you can access them directly too as parent.variable
*
* #param string $name
* #param mixed $value
*/
public function set( $name, $value )
{
if ( $this->has( $name ) ) {
// this is required because of array values
// we can have arrays defined there, so we need to remove them first
// otherwise some subvalues would to remain in the system and as a result, arrays would be merged, not overwriten by set()
$this->remove( $name );
}
$this->setNested( $name, $value );
}
/**
* remove checks even if name is not array
*
* #param string $name
*/
public function remove( $name )
{
$value = $this->get( $name );
if ( is_array( $value ) ) {
foreach ( $value as $k => $v ) {
$this->remove( $name . '.' . $k, $v );
}
}
if ( strpos( $name, '.' ) !== FALSE ) {
$parts = explode( '.', $name );
$nameTopLevel = reset( $parts );
array_shift( $parts );
$topLevelData = $this->removeKeyByAddress( $this->get( $nameTopLevel ), $parts );
ksort( $topLevelData );
$this->setNested( $nameTopLevel, $topLevelData );
}
parent::remove( $name );
}
/**
* #param array $data
* #param array $addressParts
*
* #return array
*/
private function removeKeyByAddress( $data, $addressParts )
{
$updatedLevel = & $data;
$i = 1;
foreach ( $addressParts as $part ) {
if ( $i === count( $addressParts ) ) {
unset( $updatedLevel[$part] );
} else {
$updatedLevel = & $updatedLevel[$part];
$i++;
}
}
return $data;
}
/**
* #see set()
*
* #param string $name
* #param mixed $value
*/
private function setNested( $name, $value )
{
if ( is_array( $value ) ) {
foreach ( $value as $k => $v ) {
$this->setNested( $name . '.' . $k, $v );
}
}
parent::set( $name, $value );
}
}
phpunit Test:
<?php
namespace Your\Namespace;
use Symfony\Component\DependencyInjection\Tests\ParameterBag\ParameterBagTest;
/**
* its essential to use ParameterBagNested as ParameterBag because this way we run even parent class tests upon it
* parent class is part of Symfony DIC standard test suite and we use it here just for check if our parameter bag is still ok
*/
use SBKS\DependencyInjection\ParameterBag\ParameterBagNested as ParameterBag;
/**
* testing basic and even added ParameterBag functionality
*/
class ParameterBagNestedTest extends ParameterBagTest
{
public function testConstructorNested()
{
$bag = new ParameterBag(
array(
'foo' => array( 'foo1' => 'foo' ),
'bar' => 'bar',
)
);
$this->assertEquals(
array(
'foo.foo1' => 'foo',
'foo' => array(
'foo1' => 'foo',
),
'bar' => 'bar',
),
$bag->all(),
'__construct() takes an array of parameters as its first argument'
);
}
public function testRemoveNested()
{
$bag = new ParameterBag(
array(
'foo' => array(
'foo1' => array(
'foo11' => 'foo',
'foo12' => 'foo',
),
'foo2' => 'foo',
),
'bar' => 'bar',
)
);
$bag->remove( 'foo.foo1.foo11' );
$this->assertEquals(
array(
'foo' => array(
'foo1' => array(
'foo12' => 'foo',
),
'foo2' => 'foo',
),
'foo.foo1' => array( 'foo12' => 'foo' ),
'foo.foo1.foo12' => 'foo',
'foo.foo2' => 'foo',
'bar' => 'bar',
),
$bag->all(),
'->remove() removes a parameter'
);
$bag->remove( 'foo' );
$this->assertEquals(
array(
'bar' => 'bar',
),
$bag->all(),
'->remove() removes a parameter'
);
}
public function testSetNested()
{
$bag = new ParameterBag(
array(
'foo' => array(
'foo1' => array(
'foo11' => 'foo',
'foo12' => 'foo',
),
'foo2' => 'foo',
),
)
);
$bag->set( 'foo', 'foo' );
$this->assertEquals( array( 'foo' => 'foo' ), $bag->all(), '->set() sets the value of a new parameter' );
}
public function testHasNested()
{
$bag = new ParameterBag(
array(
'foo' => array(
'foo1' => array(
'foo11' => 'foo',
'foo12' => 'foo',
),
'foo2' => 'foo',
),
)
);
$this->assertTrue( $bag->has( 'foo' ), '->has() returns true if a parameter is defined' );
$this->assertTrue( $bag->has( 'foo.foo1' ), '->has() returns true if a parameter is defined' );
$this->assertTrue( $bag->has( 'foo.foo1.foo12' ), '->has() returns true if a parameter is defined' );
$this->assertTrue( $bag->has( 'foo.foo2' ), '->has() returns true if a parameter is defined' );
}
}
Then you can use it by injecting Parameter bag into ContainerBuilder:
$parameterBag = new \Your\Namespace\ParameterBagNested();
$container = new ContainerBuilder($parameterBag);
Thats all, you can use nested parameter with dot notation now in Symfony DI container.
If you are using Symfony plugin in phpstorm it will autocomplete your nested attributes with dot notation too.
A bit late, but here is a solution that worked for me.
# app/config/config.yml
twig:
globals:
my_var: category['var']
Explanation
When you import your parameters file into your app/config/config.yml file, you can automatically access all variables defined in your parameters file.
Keep in mind, when you use the structure:
# app/config/parameters.yml
parameters:
category:
var: test
You are defining a parameter called category with a value that is in turn itself a key-value pair array that contains var as key and test as value.
Update
In newer versions of symfony (3.4 - 4.2) you can now do the following:
# app/config/config.yml
twig:
globals:
custom_categories: '%categories%'
By having this parameter set up:
# app/config/parameters.yml
parameters:
categories:
- category_A
- category_B
Things to note here:
- in parameters.yml categories is an array
so to use it in a twig template, you should be able to do something like:
<ul>
{% for category in categories %}
<li> {{ category }} </li>
{% endfor %}
</ul>
Another example with objects:
# app/config/parameters.yml
parameters:
marketplace:
name: 'My Store'
address: '...'
Configuring twig variables:
# app/config/config.yml
twig:
globals:
marketplace: '%marketplace%'
Using it in twig:
...
<p>{{marketplace.name}}</p>
<p>{{marketplace.address}}</p>
...
Hope this helps! :)
Symfony's expression language syntax to the rescue! Give this a shot:
twig:
globals:
my_var: '#=parameter("category")["var"]'
To provide an alternative approach that doesn't require removing values from the setter, I override the getter method instead.
namespace NameSpaceFor\ParameterBags;
use Symfony\Component\DependencyInjection\Exception\ParameterNotFoundException;
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag;
class ParameterBagParser extends ParameterBag
{
/**
* {#inheritDoc}
*/
public function get($name, $parent = null)
{
if (null === $parent) {
$parent = $this->parameters;
}
$name = strtolower($name);
if (!array_key_exists($name, $parent)) {
if (!$name) {
throw new ParameterNotFoundException($name);
}
if (false !== strpos($name, '.')) {
$parts = explode('.', $name);
$key = array_shift($parts);
if (isset($parent[$key])) {
return $this->get(implode('.', $parts), $parent[$key]);
}
}
$alternatives = [];
foreach ($parent as $key => $parameterValue) {
$lev = levenshtein($name, $key);
if ($lev <= strlen($name) / 3 || false !== strpos($key, $name)) {
$alternatives[] = $key;
}
}
throw new ParameterNotFoundException($name, null, null, null, $alternatives);
}
return $parent[$name];
}
}
It recursively traverses through the name until it is all of the dot notations have been checked.
So it will work with arrays and scalar values.
config.yml:
parameters:
my_param:
- test
my_inherited: '%my_param.0%' #test
ContainerAware:
$container->getParameter('my_param')[0]; //test
Trick to emulate nested parameters, acccess in the yaml file:
parameters:
crawler:
urls:
a: '%crawler.urls.a%' # here the root of the rest of the tree/array
crawler.urls.a:
cat1:
- aa
- bb
cat2:
- cc
services:
xx:
class: myclass
arguments:
$urls: '%crawler.urls.a%'
In Symfony I now access the parameter('crawler') as complete tree, in service xx the subtree/array is accessible.