【问题标题】:How to implement a nice solution for multilang entity slug based routes in Symfony2如何在 Symfony2 中为基于多语言实体 slug 的路由实现一个很好的解决方案
【发布时间】:2015-01-28 17:32:30
【问题描述】:
我想创建一个简单的包来处理网站中的一些多语言页面,其中包含已翻译的 slug。
基于translatable、sluggable 和i18nrouting
- 按照文档所述实现了一个实体(页面),其中包含标题、内容、slug 字段 + 语言环境属性
- 创建了一个新页面,设置其标题和内容,然后将其翻译为
$page->setTranslatableLocale('de');,并再次使用德语值设置这些字段,以便表格中的数据看起来不错,它们都在那里
- 使用类型提示签名实现控制器:
public function showAction(Page $page)
- 在模板中生成了一些 url:
{{ path("page_show", {"slug": "test", "_locale": "en"}) }} 和 {{ path("page_show", {"slug": "test-de", "_locale": "de"}) }},路由生成良好,看起来正确(/en/test 和 /de/test-de)
- 点击它们:
只有“en”翻译有效,“de”翻译失败:
未找到 MyBundle\Entity\Page 对象。
在检索页面时如何告诉 Symfony 或 Doctrine 或任何捆绑包使用当前语言环境?我是否必须创建一个 ParamConverter 然后将自定义 DQL 放入其中以手动完成工作?
谢谢!
【问题讨论】:
标签:
symfony
routing
translation
slug
【解决方案1】:
刚刚找到了另一种我认为更好的解决方案,我将使用它!
实现了一个存储库方法并在控制器的注释中使用它:
@ParamConverter("page", class="MyBundle:Page", options={"repository_method" = "findTranslatedOneBy"})
public function findTranslatedOneBy(array $criteria, array $orderBy = null)
{
$page = $this->findOneBy($criteria, $orderBy);
if (!is_null($page)) {
return $page;
}
$qb = $this->getEntityManager()
->getRepository('Gedmo\Translatable\Entity\Translation')
->createQueryBuilder('t');
$i = 0;
foreach ($criteria as $name => $value) {
$qb->orWhere('t.field = :n'. $i .' AND t.content = :v'. $i);
$qb->setParameter('n'. $i, $name);
$qb->setParameter('v'. $i, $value);
$i++;
}
/** @var \Gedmo\Translatable\Entity\Translation[] $trs */
$trs = $qb->groupBy('t.locale', 't.foreignKey')->getQuery()->getResult();
return count($trs) == count($criteria) ? $this->find($trs[0]->getForeignKey()) : null;
}
它的一个缺点是没有针对相同翻译值的保护...
【解决方案2】:
我找到了一个我不确定的解决方案,但它确实有效。
实现了一个 PageParamConverter:
class PageParamConverter extends DoctrineParamConverter
{
const PAGE_CLASS = 'MyBundle:Page';
public function apply(Request $request, ParamConverter $configuration)
{
try {
return parent::apply($request, $configuration);
} catch (NotFoundHttpException $e) {
$slug = $request->get('slug');
$name = $configuration->getName();
$class = $configuration->getClass();
$em = $this->registry->getManagerForClass($class);
/** @var \Gedmo\Translatable\Entity\Translation $tr */
$tr = $em->getRepository('Gedmo\Translatable\Entity\Translation')
->findOneBy(['content' => $slug, 'field' => 'slug']);
if (is_null($tr)) {
throw new NotFoundHttpException(sprintf('%s object not found.', $class));
}
$page = $em->find($class, $tr->getForeignKey());
$request->attributes->set($name, $page);
}
return true;
}
public function supports(ParamConverter $configuration)
{
$name = $configuration->getName();
$class = $configuration->getClass();
return parent::supports($configuration) && $class == self::PAGE_CLASS;
}
}
【解决方案3】:
TranslationWalker 很好地获取了活动语言环境中的实体:
class PagesRepository extends \Doctrine\ORM\EntityRepository
{
public function findTranslatedBySlug(string $slug)
{
$queryBuilder = $this->createQueryBuilder("p");
$queryBuilder
->where("p.slug = :slug")
->setParameter('slug', $slug)
;
$query = $queryBuilder->getQuery();
$query->setHint(
Query::HINT_CUSTOM_OUTPUT_WALKER,
'Gedmo\\Translatable\\Query\\TreeWalker\\TranslationWalker'
);
return $query->getSingleResult();
}
}
在控制器中
/**
* @Entity("page", expr="repository.findTranslatedBySlug(slug)")
* @param $page
*
* @return Response
*/
public function slug(Pages $page)
{
// thanks to @Entity annotation (Sensio\Bundle\FrameworkExtraBundle\Configuration\Entity)
// Pages entity is automatically retrieved by slug
return $this->render('content/index.html.twig', [
'page' => $page
]);
}