Serialize translateble entity - symfony

For translate my entity product use next code
use Knp\DoctrineBehaviors\Model as ORMBehaviors;
class Product
{
/**
* #Groups({"prod_translate"})
*/
use ORMBehaviors\Translatable\Translatable;
/**
* #var int
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
* #Groups({"prod"})
*/
private $id;
...
Translatable create new entity ProductTranslation with table in DB product_translation with id, translatable_id, locale and translatble properties.
Translation work perfectly. In page just write {{ product.translation.en/sp.name }}
When use ajax pagenation use serializer for return data.
Tried JMS\Serializer\SerializerBuilder and Symfony\Component\Serializer\Serializer
use Symfony\Component\Serializer\Serializer;
use Symfony\Component\Serializer\Normalizer\ObjectNormalizer;
use Symfony\Component\Serializer\Mapping\Factory\ClassMetadataFactory;
use Doctrine\Common\Annotations\AnnotationReader;
$products = $em->getRepository('AppBundle:Product')->findNextPageByBrandCategoryIds( array($cat), $brand, $offset, $limit );
$classMetadataFactory = new ClassMetadataFactory(new AnnotationLoader(new AnnotationReader()));
$normalizer = new ObjectNormalizer($classMetadataFactory);
$normalizer->setCircularReferenceHandler(function ($object) {
return $object->getID();
});
$serializer = new Serializer(array($normalizer));
$jsonObject = $serializer->normalize($products, 'json', array('groups' => array('prod', 'prod_translate')) );
Without groups serializet return "Maximum execution time 180second".
With groups return only Product data without translations.
What annotation or else manipulation must do for fix this issue? Please help!
Edit:
Tried Use next queries:
public function findNextPageByBrandCategoryIds(array $cat_ids, $brand_id, $offset=0, $limit=8 )
{
$qb = $this->createQueryBuilder('p');
if( !empty($cat_ids) ){
$cat_ids = implode(',', $cat_ids);
$qb->join('p.categories', 'c')
->where($qb->expr()->in('c.id', $cat_ids ) );
}
if( !empty($brand_id) ){
$qb->andWhere('p.brand = :brand')
->setParameter('brand', $brand_id);
}
if( $offset>0 ){
$qb->setFirstResult( $offset );
}
$qb->setMaxResults( $limit );
return $qb->getQuery()->getResult();
}
private function findNextPageByBrandCategoryIds($cat_id, $brand_id, $offset=0, $limit=8 )
{
$em = $this->getDoctrine()->getManager();
$sql = "SELECT p.id, t.id AS translate_id " .
"FROM product p INNER JOIN product_translation t ON t.translatable_id = p.id";
$where .= ' WHERE 1=1 ' ;
$rsm = new ResultSetMappingBuilder($em);
if( !empty($cat_id) ){
$sql .= " LEFT JOIN products_categories c ON c.product_id = p.id ";
$where .= " AND c.category_id = ".$cat_id;
}
if( !empty($brand_id) ){
$where .= " AND p.brand = ".$brand_id;
}
$limit = ' LIMIT '.$limit;
if( $offset>0 ){
$limit .= ' OFFSET '.$offset;
}
$sql = $sql.$where.$limit;
$rsm->addRootEntityFromClassMetadata('AppBundle\Entity\Product', 'p');
$rsm->addJoinedEntityFromClassMetadata('AppBundle\Entity\ProductTranslation', 't', 'p', 'product', array('id' => 'translatable_id'));
$query = $em->createNativeQuery($sql, $rsm);
return $query->getResult();
}
Debug almost every line - they are right.
If use setHint(\Doctrine\ORM\Query::HINT_FORCE_PARTIAL_LOAD, true) - work but return products only, without translations.

The Maximum execution time 180 second message is an error that has nothing to do with annotations or the serializer.
In your php.ini you have a max_execution_time option to configure the limit of a script in seconds. You can also set it with the function set_time_limit($seconds)by code, but I would not recommend to use it.
Anyway, I would bet that you are doing a very large query. How many results are you limiting?

Nik fix this issue:
On Brand, Category and Product Entities
use JMS\Serializer\Annotation\MaxDepth;
/**
* #MaxDepth(1)
*/
private $products;
/**
* #MaxDepth(1)
*/
private $categories;
/**
* #MaxDepth(1)
*/
private $brand;

Related

Symfony doctrine ManyToMany unidirectional mapping

Well, back again, i'll try to simplify my question as much as i can.
First of all, i have 2 Entities
Post
PostRating
I've created unidirectional ManyToMany relation between them, because I only need ratings to be added to each Post, if I try to map Post to PostRating too, I get Circular Reference error.
Post Entity, it creates 3rd table post_has_rating, no mapping inside PostRating Entity, It workes like expected, rating collection is added to each post, but if i want to find one rating, and edit it if needed, then it comes to be bigger headache than expected.
/**
* Post have many PostRating
* #ORM\ManyToMany(targetEntity="PostRating")
* #ORM\JoinTable(name="post_has_rating",
* joinColumns={#ORM\JoinColumn(name="user_id", referencedColumnName="id")},
* inverseJoinColumns={#ORM\JoinColumn(name="postrating_id", referencedColumnName="id", unique=true)}
* )
*/
protected $ratings;
PostController thumbAction, simple word "ratingAction"
/**
* Search related videos from youtube
* #Route("/post/thumb", name="post_thumb")
* #param Request $request
* #return string
*/
public function thumbAction (Request $request) {
$content = json_decode($request->getContent());
$serializer = $this->get('serializer');
$em = $this->getDoctrine()->getManager();
$postRatingRepo = $this->getDoctrine()->getRepository(PostRating::class);
$postRepo = $this->getDoctrine()->getRepository(Post::class);
$me = $this->getUser()->getId();
/** #var PostRating $rating */
$rating = $postRatingRepo->findOneBy(['userId' => $me]);
/** #var Post $post */
$post = $postRepo->find($content->id);
if ($post->getRatings()->contains($rating)) {
$post->removeRating($rating);
$em->remove($rating);
}
$rating = new PostRating();
$rating->setUserId($me);
switch ($content->action) {
//NVM about those hardcoded words, they are about to be removed
case 'up':
$rating->setRating(1);
break;
case 'down':
$rating->setRating(0);
break;
}
$post->addRating($rating);
$em->persist($rating);
$em->persist($post);
$em->flush();
return new JsonResponse( $serializer->normalize( ['success' => 'Post thumbs up created'] ) );
}
Problems: $rating = $postRatingRepo->findOneBy(['userId' => $me]); this row needs to have postId too for $post->getRatings()->contains($rating), right now im getting all the raitings, that I have ever created, but it Throws error if i add it, Unknown column
Should i create custom repository, so i can create something like "findRating" with DQL?
OR
Can i make Post and PostRating Entities mapped to each other more simple way, i don't really want many-to-many relation, because I don't see point of using it
Considering you want to keep OneToMany unidirectional here is my suggestion
create a custom repository for your Post Entity
namespace AppBundle\Repository;
use Doctrine\ORM\EntityRepository;
class PostRepository extends EntityRepository
{
public function findOneRatingByUser($post, $user)
{
$query = $this->createQueryBuilder('p')
->select('r')
->innerJoin('p.ratings', 'r')
->where('p.id = :post')
->andWhere('r.user = :user')
->setParameter('post', $post)
->setParameter('user', $user)
->getQuery()
;
return $query->getOneOrNullResult();
}
}
Then in your controller:
public function thumbAction (Request $request)
{
$content = json_decode($request->getContent());
$serializer = $this->get('serializer');
$em = $this->getDoctrine()->getManager();
$postRepo = $this->getDoctrine()->getRepository(Post::class);
$me = $this->getUser()->getId();
/** #var Post $post */
$post = $postRepo->find($content->id);
$rating = $postRepo->findOneRatingByUser($post->getId(), $me);
if (null === $rating) {
$rating = new PostRating();
$rating->setUserId($me);
}
switch ($content->action) {
//NVM about those hardcoded words, they are about to be removed
case 'up':
$rating->setRating(1);
break;
case 'down':
$rating->setRating(0);
break;
}
$post->addRating($rating);
$em->persist($rating);
$em->persist($post);
$em->flush();
return new JsonResponse( $serializer->normalize( ['success' => 'Post thumbs up created'] ) );
}
If you want your custom repository to work dont forget to declare it in your entity
/**
* #ORM\Entity(repositoryClass="AppBundle\Repository\PostRepository")
*/
class Post

Fatal error: Call to a member function get_results() on a non-object

I am using revolution slider plugin . I am getting above error on one of my front end form. There is one file called db.class.php in that plugin where I am getting this error. Here is the code in that file..
<?php
class UniteDBRev{
private $wpdb;
private $lastRowID;
/**
*
* constructor - set database object
*/
public function __construct(){
global $wpdb;
$this->wpdb = $wpdb;
}
/**
*
* throw error
*/
private function throwError($message,$code=-1){
UniteFunctionsRev::throwError($message,$code);
}
//------------------------------------------------------------
// validate for errors
private function checkForErrors($prefix = ""){
if(mysql_error()){
$query = $this->wpdb->last_query;
$message = $this->wpdb->last_error;
if($prefix) $message = $prefix.' - <b>'.$message.'</b>';
if($query) $message .= '<br>---<br> Query: ' . $query;
$this->throwError($message);
}
}
/**
*
* insert variables to some table
*/
public function insert($table,$arrItems){
global $wpdb;
$this->wpdb->insert($table, $arrItems);
$this->checkForErrors("Insert query error");
$this->lastRowID = $wpdb->insert_id;
return($this->lastRowID);
}
/**
*
* get last insert id
*/
public function getLastInsertID(){
global $wpdb;
$this->lastRowID = $wpdb->insert_id;
return($this->lastRowID);
}
/**
*
* delete rows
*/
public function delete($table,$where){
UniteFunctionsRev::validateNotEmpty($table,"table name");
UniteFunctionsRev::validateNotEmpty($where,"where");
$query = "delete from $table where $where";
$this->wpdb->query($query);
$this->checkForErrors("Delete query error");
}
/**
*
* run some sql query
*/
public function runSql($query){
$this->wpdb->query($query);
$this->checkForErrors("Regular query error");
}
/**
*
* insert variables to some table
*/
public function update($table,$arrItems,$where){
$response = $this->wpdb->update($table, $arrItems, $where);
if($response === false)
UniteFunctionsRev::throwError("no update action taken!");
$this->checkForErrors("Update query error");
return($this->wpdb->num_rows);
}
/**
*
* get data array from the database
*
*/
public function fetch($tableName,$where="",$orderField="",$groupByField="",$sqlAddon=""){
global $wpdb;
$query = "select * from $tableName";
if($where) $query .= " where $where";
if($orderField) $query .= " order by $orderField";
if($groupByField) $query .= " group by $groupByField";
if($sqlAddon) $query .= " ".$sqlAddon;
$response = $this->wpdb->get_results($query,ARRAY_A);
$this->checkForErrors("fetch");
return($response);
}
/**
*
* fetch only one item. if not found - throw error
*/
public function
fetchSingle($tableName,$where="",$orderField="",$groupByField="",$sqlAddon=""){
$response = $this->fetch($tableName, $where, $orderField, $groupByField, $sqlAddon);
if(empty($response))
$this->throwError("Record not found");
$record = $response[0];
return($record);
}
/**
*
* escape data to avoid sql errors and injections.
*/
public function escape($string){
$string = esc_sql($string);
return($string);
}
}
?>
I am getting this error in fetch function. I have tried with global $wpdb. But no luck. I have loaded wp-load.php also but no luck.

Symfony Doctrine : get field value in foreach loop on fetchAll()

I'm trying to display some fields of a table 'Post' using a raw query :
$connection = $em->getConnection();
$statement = $connection->prepare("SELECT * FROM Post WHERE category_id = 1");
$statement->execute();
$posts = $statement->fetchAll();
...
foreach($posts as $post) {
$xml .= $post->getId();
$xml .= $post->getTitle();
$xml .= $post->getContent();
}
I've got an error "FatalErrorException: Error: Call to a member function getId() on a non-object in ..."
All those getters are right in my Post entity. Any suggestion about what I'm doing wrong ?
[EDIT]
$em = $this->getDoctrine()->getManager();
$post_repository = $em->getRepository('MyBundle:Post');
$posts = $post_repository->findBy(array('category_id' => 1));
foreach($posts as $post) {
$xml .= $post->getTitle();
}
Returns me "Unrecognized field: category_id".
My Post class :
class Post
{
/**
* #ORM\ManyToOne(targetEntity="MyBundle\Entity\Category", inversedBy="post")
* #ORM\JoinColumns({
* #ORM\JoinColumn(name="category_id", referencedColumnName="id")
* })
*/
private $category;
/**
* Set category
*
#param MyBundle\Entity\Category $category
*/
public function setCategory(\MyBundle\Entity\Category $category)
{
$this->category = $category;
}
/**
* Get category
*
#return MyBundle\Entity\Category
*/
public function getCategory()
{
return $this->category;
}
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="title", type="string", length=255)
*/
private $title;
....
Why using directly your connection here? You should consider using the entity repository for your "posts" class. For example :
$posts = $em->getRepository('YourBundle:Post')->findBy(array('category_id' => 1));
this should work, just replace the YourBundle:Post with the proper bundle and class names. Same for the category_id, I can't guess without your implementation if it's the class property or the mapping name.
I suggest you to read more on the official Doctrine documentation to improve your knowledge on the subject.
When you execute a raw query using Doctrine's DBAL layer the results come back as an array of field names mapped to values rather than as entities.
Therefore you need something like this:
foreach($posts as $post) {
$xml .= $post['id'];
$xml .= $post['title'];
$xml .= $post['content';
}

Referencing translation inside of another translation

I have this situation:
unit:
sqkm: Square Kilometers
my_translation: Size is %size% ## I want to append the value of unit.sqkm here ##
Is there a way to reference the translation of the unit.sqkm inside the my_translation key?
Edit: Please note that i do know how i can do this via twig. My question is: is there a way to do this in the translation files.
I extended Symfony Tanslator for this:
<?php
namespace Bundle\Program\Translation;
use Symfony\Bundle\FrameworkBundle\Translation\Translator as BaseTranslator;
class Translator extends BaseTranslator
{
/**
* Uses Symfony Translator to translate, but enables referencing other translations via ##code##
*/
public function trans($id, array $parameters = array(), $domain = null, $locale = null)
{
$text = parent::trans($id, $parameters, $domain, $locale);
$translations = [];
$delimiter = "##";
$strLen = strlen($delimiter);
$pos = strpos($text, $delimiter);
while ($pos !== false) {
$startsAt = $pos + $strLen;
$endsAt = strpos($text, $delimiter, $startsAt);
$translations[] = $delimiter . substr($text, $startsAt, $endsAt - $startsAt) . $delimiter;
$pos = strpos($text, $delimiter, $endsAt + $strLen);
}
foreach ($translations as $translation) {
$translationTrim = str_replace($delimiter, '', $translation);
$text = str_replace($translation, $this->trans($translationTrim, $parameters, $domain, $locale), $text);
}
return $text;
}
}
Then replace the Symfony translator class via parameters:
parameters:
translator.class: Bundle\Program\Translation\Translator
Now you can reference other translations via ##other.translation## INSIDE your yml file.
In your Twig template, try this :
{{ 'my_translation' | trans({'%size%': size, 'unit.sqkm' : ('unit.sqkm'|trans)}) }}
You can use translated values inside other translations.
{{ 'paragraph' | trans({ '%size%': 3, '%unit%': 'unit' | trans()}) }}
Where unit itself is another key for a translation. Your translation file however could look like this:
paragraph: Size is %size% %unit%
unit: Square Kilometers
If, like me, you want to achieve this in Symfony 4.1, here's your solution
(many thanks to #Kim's answer and #Aurelijus Rozenas's answer, all credits -apart my 4 hours trying that- go to them)
First, create your new Translator class (here: src/Common/ReferenceTranslator.php)
I can't manage to find a solution where I would extend the base translator class straight in the class code, let me know if you have one
# src/Common/ReferenceTranslator.php
namespace App\Common;
use Symfony\Component\Translation\TranslatorBagInterface;
use Symfony\Component\Translation\TranslatorInterface;
class ReferenceTranslator implements TranslatorInterface, TranslatorBagInterface
{
/** #var TranslatorBagInterface|TranslatorInterface */
protected $translator;
/**
* #param TranslatorInterface|TranslatorBagInterface $translator
*/
public function __construct($translator)
{
$this->translator = $translator;
}
/**
* Uses Symfony Translator to translate, but enables referencing other translations via ##code##
* #param $id
* #param array $parameters
* #param null $domain
* #param null $locale
* #return mixed|string
*/
public function trans($id, array $parameters = array(), $domain = null, $locale = null)
{
$text = $this->translator->trans($id, $parameters, $domain, $locale);
$translations = [];
$delimiter = "##";
$strLen = strlen($delimiter);
$pos = strpos($text, $delimiter);
while ($pos !== false) {
$startsAt = $pos + $strLen;
$endsAt = strpos($text, $delimiter, $startsAt);
$translations[] = $delimiter . substr($text, $startsAt, $endsAt - $startsAt) . $delimiter;
$pos = strpos($text, $delimiter, $endsAt + $strLen);
}
foreach ($translations as $translation) {
$translationTrim = str_replace($delimiter, '', $translation);
$text = str_replace($translation, $this->trans($translationTrim, $parameters, $domain, $locale), $text);
}
return $text;
}
/**
* #param string $id
* #param int $number
* #param array $parameters
* #param null $domain
* #param null $locale
*
* #return string
*/
public function transChoice($id, $number, array $parameters = [], $domain = null, $locale = null)
{
return $this->translator->transChoice($id, $number, $parameters, $domain, $locale);
}
/**
* #param string $locale
*/
public function setLocale($locale)
{
$this->translator->setLocale($locale);
}
/**
* #return string
*/
public function getLocale()
{
return $this->translator->getLocale();
}
/**
* #param string|null $locale
*
* #return \Symfony\Component\Translation\MessageCatalogueInterface
*/
public function getCatalogue($locale = null)
{
return $this->translator->getCatalogue($locale);
}
}
And then edit your services.yaml file
# app/config/services.yml
#[...]
services:
#[...]
# Custom Translator (References)
# Overrides the Translator Service (is still available as #app.decorating_translator.inner)
app.decorating_translator:
class: App\Common\ReferenceTranslator
decorates: translator
arguments:
- '#app.decorating_translator.inner'
public: false
And voilĂ  !
Again, don't hesitate if you know how to improve this, as I'm not a Symfony expert !
The TBG answer works,
for Symfony 4.4 here is the code in service.yaml
Services
App\Common\ReferenceTranslator:
arguments:
$translator: '#translator'
And dont forget to implement LocaleAwareInterface too !

Wordpress - GD Star Rating - Set Rating after wp_insert_post

is it possbile to rate the new post (with admin account) after this?
$post_id = wp_insert_post( $my_post, $wp_error );
According to this post of the Wordpress support forum, what you should do is something like this:
function set_rating($post_id, $vote) { // $vote = 0..10
$admin = get_user_by('login', 'admin');
if ($admin !== false) {
$ip = $_SERVER['SERVER_ADDR'];
$ua = $_SERVER['HTTP_USER_AGENT'];
gdsrBlgDB::save_vote($post_id, $admin->ID, $ip, $ua, $vote);
}
return $admin;
}
You need to add a hook after that happens. First write a function and then this:
add_action('wp_insert_post', 'set_star_rating');
function set_star_rating() {
global $post;
$post = ...
}
There is also save_posts hook which happens while you're saving the post and is better documented.
This may be useful for doing some custom development based on GD Star Rating Plugin. Find my functions to 'save post like', to get 'like count for a post' and to 'check whether current user liked a post'. This is working for custom Post Types as well.
/**
* Function to save post like
* #param type $post_id
* #param type $user_id
*/
function save_post_like($post_id, $user_id) {
$ip = $_SERVER['SERVER_ADDR'];
$ua = $_SERVER['HTTP_USER_AGENT'];
if(has_user_liked_post($post_id) == 0)
gdsrBlgDB::save_vote_thumb($post_id, $user_id, $ip, $ua, 1);
}
/**
* Function to check if user like the post
* #global type $wpdb
* #global type $table_prefix
* #param type $post_id
* #return type
*/
function has_user_liked_post($post_id) {
global $wpdb, $table_prefix;
$userdata = wp_get_current_user();
$user_id = is_object($userdata) ? $userdata->ID : 0;
$sql = "SELECT * FROM " . $table_prefix . "gdsr_votes_log WHERE vote_type = 'artthumb' AND id = " . $post_id . " AND user_id = " . $user_id;
$results = $wpdb->get_row($sql, OBJECT);
if (count($results))
return 1;
else
return 0;
}
/**
* Function to get total Likes of a Post
* #global type $wpdb
* #global type $table_prefix
* #param type $post_id
* #return type
*/
function get_post_like_count($post_id) {
global $wpdb, $table_prefix;
$sql = "SELECT * FROM " . $table_prefix . "gdsr_data_article WHERE post_id = " . $post_id;
$results = $wpdb->get_row($sql, OBJECT);
if (count($results))
return $results->user_recc_plus;
else
return 0;
}

Resources