【问题标题】:Knp DoctrineBehaviors throws ContextErrorException: Warning: call_user_func_array() expects parameter 1 to be a valid callbackKnp DoctrineBehaviors throws ContextErrorException: Warning: call_user_func_array() 期望参数 1 是有效的回调
【发布时间】:2014-03-15 23:37:00
【问题描述】:

我使用 symfony2.4 和 KNP 学说行为可翻译。

我有实体 Site(用于 ID、主机、已启用)和实体 SiteTranslation(用于翻译字段:名称、描述……)。

我使用查询来获取结果

$qb = $this->createQueryBuilder('s')
    ->addSelect('translation') // to eager fetch translations (optional)
    ->leftJoin('s.translations', 'translation') // or innerJoin ?
    ->orderBy('s.root', 'ASC')
    ->addOrderBy('s.lft', 'ASC');

我想在 Twig 中打印结果。对于来自Site 实体的 ID、主机和启用字段,这很简单:

{{ item.id }}

但我无法打印翻译后的字段(名称、描述……)

{{ item.name }}

它不起作用。

错误信息:

ContextErrorException: 警告:call_user_func_array() 期望参数 1 是有效的 >callback,类 'Net\ConBundle\Entity\SiteTranslation' 在 >D:\Users...\vendor\ 中没有方法 'name' knplabs\doctrine->behaviors\src\Knp\DoctrineBehaviors\Model\Translatable\TranslatableMethods.php 第 140 行

可翻译字段的获取器和设置器位于 SiteTranslation 实体中。

更新:

我仍然没有找到错误的解决方案。

这里是Site 实体:

<?php
namespace Pnet\ConlocoBundle\Entity;

use Symfony\Component\Validator\Mapping\ClassMetadata;
use Symfony\Component\Validator\Constraints as Assert;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
use Gedmo\Mapping\Annotation as Gedmo;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\HttpFoundation\File\UploadedFile;
use Knp\DoctrineBehaviors\Model as ORMBehaviors;


 /**
 * @UniqueEntity("host", message="site.host.unique", groups={"edit"})
 * @Gedmo\Tree(type="nested")
 * @ORM\Entity(repositoryClass="Pnet\ConlocoBundle\Entity\Repository\SiteRepository")
 * @ORM\Table(name="site")
 */
class Site
{
use ORMBehaviors\Translatable\Translatable;   // knp translatable strategy

/**
 * @ORM\Id
 * @ORM\Column(type="integer")
 * @ORM\GeneratedValue(strategy="AUTO")
 */
protected $id;


/**
 * @ORM\Column(type="string", length=40, unique=true)
 * @Assert\NotBlank(message="site.host.notBlank", groups={"edit"})
 * @Assert\Length(max = "40", maxMessage = "site.host.maxLength", groups={"edit"})
 */    
protected $host;

/**
 * @ORM\Column(type="string", length=255, nullable=true)
 * @Assert\Image()
 */
protected $image;     

/**
 * @ORM\Column(type="boolean")
 * @Assert\Choice(choices = {"1", "0"}, message = "site.isDefault.choice", groups={"edit"})
 */
protected $isDefault;

/**
 * @ORM\Column(type="boolean")
 * @Assert\Choice(choices = {"1", "0"}, message = "site.enabled.choice", groups={"edit"})
 */  
protected $enabled;

/**
 * @ORM\Column(type="string", length=64, nullable=true)
 */
protected $analytics;     

/**
 * @Gedmo\TreeLeft
 * @ORM\Column(name="lft", type="integer")
 */
private $lft;

/**
 * @Gedmo\TreeLevel
 * @ORM\Column(name="lvl", type="integer")
 */
private $lvl;

/**
 * @Gedmo\TreeRight
 * @ORM\Column(name="rgt", type="integer")
 */
private $rgt;

/**
 * @Gedmo\TreeRoot
 * @ORM\Column(name="root", type="integer", nullable=true)
 */
private $root;

/**
 * @Gedmo\TreeParent
 * @ORM\ManyToOne(targetEntity="Site", inversedBy="children")
 * @ORM\JoinColumn(name="parent_id", referencedColumnName="id", onDelete="CASCADE")
 */
private $parent;

/**
 * @ORM\OneToMany(targetEntity="Site", mappedBy="parent")
 * @ORM\OrderBy({"lft" = "ASC"})
 */
private $children;


private $file;

public $idByFilter;

public $nameByFilter;    


/**
 * Proxy translations (Knp/Doctrine Behaviors)
 * An extra feature allows you to proxy translated fields of a translatable entity.
 * You can use it in the magic __call method of you translatable entity so that when
 * you try to call getName (for example) it will return you the translated value
 * of the name for current locale:  
 */
public function __call($method, $arguments)
{
    return $this->proxyCurrentLocaleTranslation($method, $arguments);
}  







/**
 * Get id
 *
 * @return integer 
 */
public function getId()
{
    return $this->id;
}

/**
 * Set host
 *
 * @param string $host
 * @return Site
 */
public function setHost($host)
{
    $this->host = $host;

    return $this;
}

/**
 * Get host
 *
 * @return string 
 */
public function getHost()
{
    return $this->host;
}

/**
 * Set isDefault
 *
 * @param boolean $isDefault
 * @return Site
 */
public function setIsDefault($isDefault)
{
    $this->isDefault = $isDefault;

    return $this;
}

/**
 * Get isDefault
 *
 * @return boolean 
 */
public function getIsDefault()
{
    return $this->isDefault;
}

/**
 * Set enabled
 *
 * @param boolean $enabled
 * @return Site
 */
public function setEnabled($enabled)
{
    $this->enabled = $enabled;

    return $this;
}

/**
 * Get enabled
 *
 * @return boolean 
 */
public function getEnabled()
{
    return $this->enabled;
}

/**
 * Set analytics
 *
 * @param string $analytics
 * @return Site
 */
public function setAnalytics($analytics)
{
    $this->analytics = $analytics;

    return $this;
}

/**
 * Get analytics
 *
 * @return string 
 */
public function getAnalytics()
{
    return $this->analytics;
}    

/**
 * Get ID from Filter
 *
 * @return string 
 */    
public function getIdByFilter()
{
    return $this->idByFilter;
}       

/**
 * Get name from Filter
 *
 * @return string 
 */    
public function getNameByFilter()
{
    return $this->nameByFilter;
} 

/**
 * Set image
 *
 * @param string $image
 * @return Site
 */
public function setImage($image)
{
    $this->image = $image;

    return $this;
}

/**
 * Get image
 *
 * @return string 
 */
public function getImage()
{
    return $this->image;
}

 /**
 * Set file
 *
 * @param string $file
 * @return Site
 */
public function setFile($file)
{
    $this->file = $file;

    return $this;
}

/**
 * Get file
 *
 * @return string 
 */
public function getFile()
{
    return $this->file;
} 


/**
 * 
 * Tree functions
 */
public function setParent(Site $parent = null)
{
    $this->parent = $parent;
}

public function getParent()
{
    return $this->parent;
}

public function getRoot()
{
    return $this->root;
}

public function getLvl()
{
    return $this->lvl;
}

public function getChildren()
{
    return $this->children;
}

public function getLft()
{
    return $this->lft;
}

public function getRgt()
{
    return $this->rgt;
}

/**
 * Add a method to the entity class that shows the name indented by nesting level
 */
public function getLeveledName()
{
    return str_repeat(
        html_entity_decode('&nbsp;', ENT_QUOTES, 'UTF-8'),
        ($this->getLvl()) * 3
    ) . $this->getName();
}
public function getLeveledPosition()
{
    return str_repeat(
        html_entity_decode('&nbsp;', ENT_QUOTES, 'UTF-8'),
        ($this->getLvl()) * 3
    );
}  
}

这里是SiteTranslation 实体:

namespace Pnet\ConlocoBundle\Entity;

use Symfony\Component\Validator\Constraints as Assert;
use Doctrine\ORM\Mapping as ORM;
use Knp\DoctrineBehaviors\Model as ORMBehaviors;

/**
 * @ORM\Entity
 */
class SiteTranslation
{
use ORMBehaviors\Translatable\Translation;

/**
* @ORM\Column(type="string", length=60)
* @Assert\NotBlank(message="site.name.notBlank", groups={"edit"})
* @Assert\Length(max = "60", maxMessage = "site.name.maxLength", groups={"edit"})
*/ 
protected $name; 

/**
 * @ORM\Column(type="string", length=100)
 * @Assert\NotBlank(message="site.title.notBlank", groups={"edit"})
 * @Assert\Length(max = "100", maxMessage = "site.title.maxLength", groups={"edit"})
 */     
protected  $title;  

/**
 * @ORM\Column(type="string", length=200)
 * @Assert\NotBlank(message="site.longTitle.notBlank", groups={"edit"})
 * @Assert\Length(max = "200", maxMessage = "site.longTitle.maxLength", groups={"edit"})
 */ 
protected  $longTitle;

/**
 * @ORM\Column(type="string", length=250, nullable=true)
 * @Assert\Length(max = "250", maxMessage = "site.keywords.maxLength", groups={"edit"})
 */     
protected  $keywords;

/**
 * @ORM\Column(type="string", length=500, nullable=true)
 * @Assert\Length(max = "500", maxMessage = "site.description.maxLength", groups={"edit"})
 */       
protected  $description;


/**
 * Set name
 *
 * @param string $name
 * @return Site
 */
public function setName($name)
{
    $this->name = $name;

    return $this;
}

/**
 * Get name
 *
 * @return string 
 */
public function getName()
{
    return $this->name;
}


/**
 * Set title
 *
 * @param string $title
 * @return Site
 */
public function setTitle($title)
{
    $this->title = $title;

    return $this;
}

/**
 * Get title
 *
 * @return string 
 */
public function getTitle()
{
    return $this->title;
}

/**
 * Set longTitle
 *
 * @param string $longTitle
 * @return Site
 */
public function setLongTitle($longTitle)
{
    $this->longTitle = $longTitle;

    return $this;
}

/**
 * Get longTitle
 *
 * @return string 
 */
public function getLongTitle()
{
    return $this->longTitle;
}

/**
 * Set keywords
 *
 * @param string $keywords
 * @return Site
 */
public function setKeywords($keywords)
{
    $this->keywords = $keywords;

    return $this;
}

/**
 * Get keywords
 *
 * @return string 
 */
public function getKeywords()
{
    return $this->keywords;
}

/**
 * Set description
 *
 * @param string $description
 * @return Site
 */
public function setDescription($description)
{
    $this->description = $description;

    return $this;
}

/**
 * Get description
 *
 * @return string 
 */
public function getDescription()
{
    return $this->description;
}
}

【问题讨论】:

    标签: symfony doctrine twig translation


    【解决方案1】:

    您尚未将所有可翻译的属性/方法移至您的 &lt;Name&gt;Translation 类。

    异常清楚地表明您的 SiteTranslation 类中没有 name/getName 方法。

    请阅读my answer over here 了解如何正确使用 Knp\DoctrineBehaviors 的魔法翻译代理。

    【讨论】:

    • 在站点实体中我没有可翻译的属性/方法。我已将它们全部移至 SiteTranslation。在 SiteTranslation 中,我保护了 $name;、公共函数 setName($name) 和公共函数 getName()。在站点实体中,我有 __call: return $this->proxyCurrentLocaleTranslation($method, $arguments);
    • 您能否尝试将翻译类中$name 属性的可见性从protected 更改为public 并报告回来?
    • 我改成 public $name 但还是不行。错误信息是一样的。
    【解决方案2】:

    TL;DR:

    作为(可能是肮脏的)解决方法,使用

    {{ item.getName }}
    

    在您的 Twig 模板中,而不是

    {{ item.name }}
    

    说明:

    我遇到了同样的问题,我认为当与 Twig 一起使用时,这应该被视为 Knp DoctrineBehaviors 文档中的一个错误。当您在 Twig 模板中调用它时:

    {{ item.name }}
    

    这是 Twig 在后台为获取 name 属性所做的工作:

    1. 尝试获取item对象的name公共属性
    2. 如果未找到,则检查item 对象的name 公共方法
    3. 如果没有找到,检查“item”对象的getName()公共方法
    4. 如果未找到,则检查 __call() 魔术方法(并使用 name 参数调用它)

    这里的问题是第 4 步。您定义的神奇的 __call() 方法(按照官方 DoctrineBehaviors 文档的建议)是使用 name 参数而不是 getName 调用的。然后它调用proxyCurrentLocaleTranslation() 方法,该方法试图调用您的翻译类的name 公共方法。当然,它不存在,因为你只有一个getName() 方法。

    在 Twig 中查看此问题:https://github.com/twigphp/Twig/issues/342

    通过直接使用 Twig 中的{{ item.getName }} 代码,将调用正确的方法名称。

    【讨论】:

    • 我同意你的观点,我也使用过 {{ item.getName }}
    【解决方案3】:

    这对我有用:

    public function __call($method, $arguments)
    {
        try {
            return $this->proxyCurrentLocaleTranslation($method, $arguments);
        } catch (\Symfony\Component\Debug\Exception\ContextErrorException $e) {
            return $this->proxyCurrentLocaleTranslation('get' . ucfirst($method), $arguments);
        }
    
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-04-18
      • 2013-08-01
      • 2015-10-18
      • 2012-10-04
      • 1970-01-01
      • 2016-06-06
      • 1970-01-01
      • 2016-10-29
      相关资源
      最近更新 更多