twig json_encode empty json - json

I'm receiving a list of elements from an API. All the elements are well formatted. When I dump one of them using twig, I get the following :
Leg {#2695 ▼
-id: null
#reservation: null
-airportStart: "AIX LES MILLES"
-airplaneType: "Cessna Citation Mustang"
-airportEnd: "ROBINSON"
-startDate: "2015-09-10 20:00:00"
-startHour: "2015-09-10 20:00:00"
-endHour: "2015-09-10 21:00:21"
-durationLeg: "01:21"
#nbPax: "4"
-price: null
-updatedPrice: null
-discountOnLeg: null
-tva: null
-status: null
}
My user must select one of these elements, So what I'm trying to do is to send the encoded json back to the controller, using
{{ element|json_encode }}
Unfortunately, the json is empty. When I try to dump the encoded json using
{{ dump(element|json_encode) }}
all I get is an empty array {};
Any idea why Is there another way to send the selected element datas to a controller function? (These elements are not persisted, each call on the API returns thousands of results)

I'm little late to the party (2 years of lateness), but for any one like me coming from a google research i say : i had the same problem too, and after googling around, i "solved" my problem with the Serializer Component. How? let me show you!
Installation
php composer.phar require symfony/serializer
Entity
<?php
namespace Your\Namespace\Entity;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Serializer\Serializer;
use Symfony\Component\Serializer\Encoder\JsonEncoder;
use Symfony\Component\Serializer\Normalizer\ObjectNormalizer;
/**
* Leg
*
* #ORM\Table()
* #ORM\Entity(repositoryClass="Your\Namespace\Entity\LegRepository")
*/
class Leg {
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
...
public function serializer()
{
$encoder = new JsonEncoder();
$normalizer = new ObjectNormalizer();
$normalizer->setIgnoredAttributes(array(
'whatever', 'attributes', 'you', 'want', 'to', 'ignore'
));
// The setCircularReferenceLimit() method of this normalizer sets the number
// of times it will serialize the same object
// before considering it a circular reference. Its default value is 1.
$normalizer->setCircularReferenceHandler(function ($object) {
return $object->getName();
});
$serializer = new Serializer(array($normalizer), array($encoder));
return $serializer->serialize($this, 'json');
}
}
Twig
{{ Leg.serializer|raw }}
N.B : this is tested under symfony 2.6

Related

Doctrine: addSelect method creates undesired seperate object (Symfony)

I want to query the entity based on a calculated property that does not exist in the database or on the entity.
If I run
return $this->createQueryBuilder('b')
->select('b')
->addSelect(
'... as extra_property'
)
->having('extra_property = :param')
->setParameter('param', $param)
->orderBy('extra_property', 'ASC')
->getQuery()
->getResult();
This results in a collection with each entity in the following format:
"0": {}, // The 9 entity properties
"extra_property": "value"
However, I want the extra_property to be added to the other entity properties as the tenth property. How do I fix this?
The problem lies in the getResult() method, the default hydration method is "HYDRATE_OBJECT" which will try to hydrate the output as the defined entity, which does not know about your extra property.
public function getResult($hydrationMode = self::HYDRATE_OBJECT)
{
return $this->execute(null, $hydrationMode);
}
Depending on what your other properties are, you could just use the "HYDRATE_SCALAR" option which will just give you a flat output of the results.
If you other properties are nested entities, you will have to manually select the fields in your select to bypass the hydration process, or find a way to add that property to your entity and tell the hydrator to get that data from a dedicated query or something.
You can find the different hydration methods as constants of the AbstractQuery class, you can read more on the Doctrine documentation
/**
* Hydrates an object graph. This is the default behavior.
*/
public const HYDRATE_OBJECT = 1;
/**
* Hydrates an array graph.
*/
public const HYDRATE_ARRAY = 2;
/**
* Hydrates a flat, rectangular result set with scalar values.
*/
public const HYDRATE_SCALAR = 3;
/**
* Hydrates a single scalar value.
*/
public const HYDRATE_SINGLE_SCALAR = 4;
/**
* Very simple object hydrator (optimized for performance).
*/
public const HYDRATE_SIMPLEOBJECT = 5;
/**
* Hydrates scalar column value.
*/
public const HYDRATE_SCALAR_COLUMN = 6;

PyroCMS(Laravel) where clause within the translations not working correctly

I have been struggling with this for quite a while. I use PyroCMS and it has a Posts module that has all the fields in the database and all that and if you want to find a specific post, you can just use a normal WHERE clause and find a post by a date and so on.
But if a field is checked in CMS as translatable, I can't access that field and use it to find a post, because the CMS creates another field in another table that is called posts_translations, and it contains all the fields that are translatable. Usually that is a simple $posts->where("field","value"), but the field doesn't exist if it's translatable.
So I tried to use whereHas, but it doesn't really return anything.
public function meklet(PostRepositoryInterface $posts, $q)
{
$postss = $posts->all()->whereHas('translations', function($query) use($q) {
$query = $query->where(function($query) use($q) {
$query->where('title', 'like', '%'.$q.'%');
});
});
die(var_dump($q));
return $this->view->make("mendo.module.report::reports/search");
}
As you can see I use PostRepositoryInterface maybe I need to use some other class to access what I want? Im very confused, I know its a laravel base, but I can't really wrap my head around this simple problem.
You shouldn't use one letter variables and too much nested functions there:
/**
* Searches for all matches.
*
* #param PostRepositoryInterface $posts The posts
* #param string $search The search
* #return View
*/
public function search(PostRepositoryInterface $posts, $search)
{
/* #var PostCollection $results */
$results = $posts->all()->filter(
function (PostInterface $post) use ($search) {
return str_contains(
strtolower($post->getFieldValue('title')),
strtolower($search)
);
}
);
dd($results);
return $this->view->make('mendo.module.report::reports/search', [
'posts' => $results,
]);
}
And route should be like:
'posts/search/{search}' => [
'as' => 'anomaly.module.posts::posts.search',
'uses' => 'Anomaly\PostsModule\Http\Controller\PostsController#search',
],
To use a DB query directly you need to write translations join self. It is not so difficult.

JMS_Serializer service not working in Symfony2 - Error: Allowed memory size

In my Controller in Symfony2 I am retrieving a collection of Blog Posts as Doctrine Entities:
$posts = $this->getDoctrine()
->getRepository('MyBundle:Post')
->findPostDatesAscending();
I dutifully installed the JMSSerializer bundle as it is supposed to work with Doctrine ORM, and called the service also in my Controller:
$serializer = $this->container->get('jms_serializer');
$jsonContent = $serializer->serialize($posts, 'json');
echo $jsonContent;
However, I get the following error:
FatalErrorException: Error: Allowed memory size of 134217728 bytes exhausted (tried to allocate 55204 bytes) in C:\wamp\www\Symfony\vendor\jms\serializer\src\JMS\Serializer\GenericSerializationVisitor.php line 58
I tried to find this issue already, and there seems to be not much joy in getting serialization of Entities to JSON to work. I'm sure this is some sort of recursion issue, but I am pretty new to Symfony2 so am unsure of the reasons why this is the case, and in particular, how I can get these Entities/Objects into a JSON string.
I almost forgot, my post object has a oneToMany relationship. Not sure if this is relevant:
/BlogBundle/Resources/config/doctrine/Blog.orm.yml
MyBundle\BlogBundle\Entity\Blog:
type: entity
repositoryClass: MyBundle\BlogBundle\Entity\BlogRepository
oneToMany:
posts:
targetEntity: Post
mappedBy: blog
table: null
fields:
id:
type: integer
id: true
generator:
strategy: AUTO
blog:
type: string
length: '255'
uniqueConstraints:
search_idx:
columns: blog
lifecycleCallbacks: { }
And my blog object is also mapped to an author object - typical blog set up really...
EDIT: In response to #kix, I added exclusion code to ALL of my Entity Classes (Post, see example code below, Blog and Author), which excluded all of the objects to be serialized and STILL got the memory error. I limited the response to just one blog post, which is only linked to a blog (id and name) and author (id and name).
namespace MyBundle\BlogBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use JMS\Serializer\Annotation\ExclusionPolicy;
use JMS\Serializer\Annotation\Expose;
/**
* The following annotations tells the serializer to skip all properties which
* have not marked with #Expose.
*
* #ExclusionPolicy("all")
*/
/**
* Post
*/
class Post
{
/**
* #var integer
*/
private $id;
...etc
Any help will be appreciated, thank-you.
You should try using JMS Serializer's exclusion strategies to specify which properties should be serialized and which should not be.
I suppose your object has some nested properties which make the serialization too complicated (maybe it fetches the post's blogs, and then tries to serialize this blog's other posts, and so on).
What happens if you just:
$posts = $this->getDoctrine()->getRepository('MyBundle:Post')->findPostDatesAscending();
return array('posts' => $posts);
Tried to comment but don't have the reputation.
In your repository make select statement only on the needed ones.Avoided including foreign keys ,relationship fields.A working example
public function getAllVotersDesc()
{
//return $this->findBy(array(), array('id' => 'DESC'));
return $this
->createQueryBuilder('v')
->select('v.firstname','lastname')
// ->where('v.dateCreated <= :now')->setParameter('now', new \DateTime())
->orderBy('v.dateCreated', 'DESC')
// ->setMaxResults(10)
->getQuery()
->getResult()
;
}

Symfony2 tree builder - what does the method canBeUnset() do?

$rootNode
->children()
->arrayNode('form')
->info('form configuration')
->canBeUnset()
->treatNullLike(array('enabled' => true))
->treatTrueLike(array('enabled' => true))
->children()
->booleanNode('enabled')->defaultTrue()->end()
->end()
->end()
Line 5 of the above snippet from Symfony\Bundle\FrameworkBundle\DependencyInjection\Configuration uses the method canBeUnset(). I don't know what this does because it seems to not do anything if I remove it. I'm working understanding semantic configuration for my own bundles.
Following the code, you can find definition for this method in Symfony/Component/Config/Definition/Builder/ArrayNodeDefinition class.
/**
* Sets whether the node can be unset.
*
* #param Boolean $allow
*
* #return ArrayNodeDefinition
*/
public function canBeUnset($allow = true)
{
$this->merge()->allowUnset($allow);
return $this;
}
This is passed to MergeBuilder ( Symfony/Component/Config/Definition/Builder/MergeBuilder ) which handles config merging.
/**
* Sets whether the node can be unset.
*
* #param Boolean $allow
*
* #return MergeBuilder
*/
public function allowUnset($allow = true)
{
$this->allowFalse = $allow;
return $this;
}
So my understanding is, that this method defines, if your config value can be unset while merging configurations, in case the overriding config does not support the value. I would have to test though, to find out the behaviour if the unsetting is not allowed, but I guess then it would throw an exception about a missing config value just like isRequired.

CakePhp TranslateBehavior, validate and save multiple locale

Context:
I Want to create a web application using CakePhp which should be translatable. I want to save multiple translations for the same field in one form.
Problem:
I've tried a dozen ways to get this to work and I did. But I ended up using two custom SQL queries which really doesn't feel like a cakePhp solution.
Question:
Does anybody know a better way to achieve the same result?
What I tried:
Giving the form fields a name like 'Model.fieldName.locale', which gives it the right format in the name attr of the input element but then my validation doesn't recognize the field name. But saving works.
Giving the form fields a name like 'modelLocale' and pass in a name attr 'data[Model][field][locale]', in this case the validation works exept for isUnique but saving to the database doesn't work.
More variations of this but not worth mentioning.
I'll add my view and model below: (if u want to see more code or need more info just ask)
/App/View/Category/add.ctp
<?php echo $this->Form->create(); ?>
<?php echo $this->Form->input('title|dut'); ?>
<?php echo $this->Form->input('title|eng'); ?>
<?php echo $this->Form->input('title|fre'); ?>
<?php echo $this->Form->input('description|dut', array('type'=>'textarea')); ?>
<?php echo $this->Form->input('description|eng', array('type'=>'textarea')); ?>
<?php echo $this->Form->input('description|fre', array('type'=>'textarea')); ?>
<?php echo $this->Form->end('add'); ?>
/App/Model/AppModel.php
<?php
App::uses('Model', 'Model');
class AppModel extends Model {
/**
* Check Unique
*
* Searches the i18n table to determine wetter a field is unique or not.
* Expects field name to be as following: "fieldname|locale".
*
* #param array $data The data of the field, automatically passed trough by cakePhp.
* #param string $field The name of the field, which should match the one in the view.
* #returns boolean
*/
public function checkUnique($data, $field) {
// Seperate the field key and locale which are seperated by "|".
$a = preg_split('/[|]/', $field, 2);
// If field key and locale are found...
if (is_array($a) || count($a) === 2) {
$q = sprintf("SELECT * FROM i18n WHERE i18n.locale = '%s' AND i18n.model = '%s' AND i18n.field = '%s' AND i18n.content = '%s' LIMIT 1",
Sanitize::escape($a[1]),
Sanitize::escape(strtolower($this->name)),
Sanitize::escape($a[0]),
Sanitize::escape($data[$field])
);
if ($this->query($q)) {
return false;
}
return true;
}
}
/**
* Setup Translation
*
* Loops trough the fields. If a field is translatable
* (which it will know by it's structure [fieldname]|[locale])
* and has the default locale. Then it's value will be stored
* in the array where cake expects it
* (data[Model][fieldname] instead of data[Model][fieldname|defaultLocale])
* so that cake will save it to the database.
*
* In the afterSave method the translations will be saved, for then we know
* the lastInsertId which is also the foreign_key of the i18n table.
*/
public function _setupTranslations() {
foreach($this->data[$this->name] as $key => $value) {
$a = preg_split('/[|]/', $key, 2);
if (is_array($a) && count($a) === 2) {
$languages = Configure::read('Config.languages');
if ($a[1] === $languages[Configure::read('Config.defaultLanguage')]['locale']) {
$this->data[$this->name][$a[0]] = $value;
}
}
}
}
/**
* Save Translations
*
* Saves the translations to the i18n database.
* Expects form fields with translations to have
* following structure: [fieldname]|[locale] (ex. title|eng, title|fre, ...).
*/
public function _saveTranslations() {
foreach($this->data[$this->name] as $key => $value) {
$a = preg_split('/[|]/', $key, 2);
if (is_array($a) && count($a) === 2) {
$q = sprintf("INSERT INTO i18n (locale, model, foreign_key, field, content) VALUES ('%s', '%s', '%s', '%s', '%s')",
Sanitize::escape($a[1]),
Sanitize::escape(strtolower($this->name)),
Sanitize::escape($this->id),
Sanitize::escape($a[0]),
Sanitize::escape($value)
);
$this->query($q);
}
}
}
/**
* Before Save
*/
public function beforeSave() {
$this->_setupTranslations();
return true;
}
/**
* After Save
*/
public function afterSave() {
$this->_saveTranslations();
return true;
}
}
/App/Model/Category.php
<?php
class Category extends AppModel {
public $name = 'Category';
public $hasMany = array(
'Item'=>array(
'className'=>'Item',
'foreignKey'=>'category_id',
'order'=>'Item.title ASC'
)
);
var $actsAs = array(
'Translate'=>array(
'title',
'description'
)
);
public $validate = array(
'title|dut'=>array(
'required'=>array(
'rule'=>'notEmpty',
'message'=>'Veld verplicht'
),
'unique'=>array(
'rule'=>array('checkUnique', 'title|dut'),
'message'=>'Titel reeds in gebruik'
),
),
'title|eng'=>array(
'required'=>array(
'rule'=>'notEmpty',
'message'=>'Veld verplicht'
),
'unique'=>array(
'rule'=>array('checkUnique', 'title|eng'),
'message'=>'Titel reeds in gebruik'
),
),
'title|fre'=>array(
'required'=>array(
'rule'=>'notEmpty',
'message'=>'Veld verplicht'
),
'unique'=>array(
'rule'=>array('checkUnique', 'title|fre'),
'message'=>'Titel reeds in gebruik'
),
),
);
}
?>
NOTE: There isn't that much information out there on this subject... I have a lot more questions about the translation behavior like getting the recursive results also in the correct locale, ... Anybody know a good tut or source of info (cookbook is quite limited)
Thanks for reading!!
It appears you may be building a CRM of sorts that allows the users to establish content that is read into the site based on the language they have set. I would use the built in i18n and l10n. It makes it really simple, but this is probably not a solution for dynamic content.
Having said that, the only other way I can think of doing this is very tedious. I would build a single screen with a language identifier drop down. So instead of trying to cram ALL languages in the same screen with a test box for each language, I would create one form and then use a drop down for the language.
Your model is using a column to define with language the row belongs to. The form you have created is expressing all languages in a single row. So if you were to view the Index page showing the records, of course you would see:
title 1 eng
title 1 dut
title 1 fre
title 2 eng
title 2 dut
title 2 fre
...
Further more, if you were ever to add a new language, you will have to modify the validation in the model and the form.
However, if you are set on doing it this way, change the | to _ and off you go. But then you will need to store all of the data in a single record. So when you look at the Index for the records, you will see:
title 1 end dut fre
title 2 end dut fre
...
My Advice:
1) Use the built in i18n / l10n using .po / .pot files.
2) If the content will be changing frequently and required to be stored in the database so it can be easily changed / updated frequently on the fly, then use a drop down.
Language: dropdown
Title: text_field