You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Sylius自定义Slider资源编辑页翻译值无法显示求助

我在Sylius框架中开发了自定义实体Slider及其对应的Translation,两者均已注册为Resource,但在管理后台进入Slider编辑页面时,翻译字段(link)的值无法显示。相关代码如下:

Slider实体代码

<?php

namespace App\Entity\Slider;

use ApiPlatform\Metadata\ApiResource;
use App\Entity\Trait\StatusTrait;
use App\Repository\SliderRepository;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\ORM\PersistentCollection;
use Sylius\Component\Core\Model\ImageInterface;
use Sylius\Resource\Model\TranslatableTrait;
use Sylius\Resource\Model\TranslationInterface;
use Webmozart\Assert\Assert;

#[ORM\Entity(repositoryClass: SliderRepository::class)]
#[ORM\Table(name: 'app_slider')]
#[ApiResource]
class Slider implements SliderInterface
{
    use StatusTrait, TranslatableTrait {
        __construct as private initializeTranslationsCollection;
        getTranslation as private doGetTranslation;
    }

    /** @var Collection<array-key, ImageInterface> */
    #[ORM\OneToMany(mappedBy: 'owner', targetEntity: SliderImage::class, cascade: ['persist', 'remove'], orphanRemoval: true)]
    protected Collection $images;

    /** @var ArrayCollection|PersistentCollection|TranslationInterface[] */
    #[ORM\OneToMany(mappedBy: 'translatable', targetEntity: SliderTranslation::class, cascade: ['all'], orphanRemoval: true)]
    protected $translations;

    #[ORM\Id]
    #[ORM\GeneratedValue]
    #[ORM\Column]
    private ?int $id = null;

    #[ORM\Column]
    private ?int $position = null;

    private string|null $mediaImages = null;

    public function __construct()
    {
        $this->initializeTranslationsCollection();
        $this->images = new ArrayCollection();
    }

    public function getId(): ?int
    {
        return $this->id;
    }

    public function getPosition(): ?int
    {
        return $this->position;
    }

    public function setPosition(int $position): static
    {
        $this->position = $position;

        return $this;
    }

    public function getMediaImages(): ?string
    {
        return $this->mediaImages;
    }

    public function setMediaImages(?string $mediaImages): void
    {
        $this->mediaImages = $mediaImages;
    }

    public function getImages(): Collection
    {
        return $this->images;
    }

    public function getImagesByType(string $type): Collection
    {
        return $this->images->filter(function (ImageInterface $image) use ($type): bool {
            return $type === $image->getType();
        });
    }

    public function hasImages(): bool
    {
        return !$this->images->isEmpty();
    }

    public function hasImage(ImageInterface $image): bool
    {
        return $this->images->contains($image);
    }

    public function addImage(ImageInterface $image): void
    {
        $image->setOwner($this);
        $this->images->add($image);
    }

    public function removeImage(ImageInterface $image): void
    {
        if ($this->hasImage($image)) {
            $image->setOwner(null);
            $this->images->removeElement($image);
        }
    }

    public function getTranslation(?string $locale = null): TranslationInterface
    {
        /** @var SliderTranslationInterface $translation */
        $translation = $this->doGetTranslation($locale);

        return $translation;
    }

    public function setLink(null|string $link): void
    {
        $this->getTranslation()->setLink($link);
    }

    public function getLink(): ?string
    {
        return $this->getTranslation()->getLink();
    }

    protected function createTranslation(): SliderTranslationInterface
    {
        return new SliderTranslation();
    }
}

SliderTranslation实体代码

<?php

namespace App\Entity\Slider;

use App\Repository\SliderTranslationRepository;
use Doctrine\ORM\Mapping as ORM;
use Sylius\Resource\Model\AbstractTranslation;
use Sylius\Resource\Model\TranslatableInterface;

#[ORM\Entity(repositoryClass: SliderTranslationRepository::class)]
#[ORM\Table(name: 'app_slider_translation')]
class SliderTranslation extends AbstractTranslation implements SliderTranslationInterface
{
    #[ORM\Id]
    #[ORM\GeneratedValue]
    #[ORM\Column]
    private ?int $id = null;

    #[ORM\Column(length: 255)]
    protected null|string $link = null;

    public function getId(): ?int
    {
        return $this->id;
    }

    public function getLink(): null|string
    {
        return $this->link;
    }

    public function setLink(string $link): void
    {
        $this->link = $link;
    }
}

SliderTranslationType表单代码

<?php

declare(strict_types=1);

namespace App\Form\Type\Slider;

use App\Entity\Slider\SliderTranslationInterface;
use Sylius\Bundle\ResourceBundle\Form\Type\AbstractResourceType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\Form\FormView;

class SliderTranslationType extends AbstractResourceType
{
    public function __construct(
        string $dataClass,
    ) {
        parent::__construct($dataClass);
    }

    public function buildForm(FormBuilderInterface $builder, array $options): void
    {
        $builder
            ->add('link', TextType::class, [
                'required' => true,
                'label' => 'Link',
            ])
        ;
    }

    public function getBlockPrefix(): string
    {
        return 'app_slider_translation';
    }
}

SliderType表单代码

<?php

declare(strict_types=1);

namespace App\Form\Type\Slider;

use App\Repository\SliderRepository;
use Doctrine\ORM\NonUniqueResultException;
use Doctrine\ORM\NoResultException;
use Sylius\Bundle\ResourceBundle\Form\Type\AbstractResourceType;
use Sylius\Bundle\ResourceBundle\Form\Type\ResourceTranslationsType;
use Symfony\Component\Form\Extension\Core\Type\CollectionType;
use Symfony\Component\Form\Extension\Core\Type\HiddenType;
use Symfony\Component\Form\FormBuilderInterface;

class SliderType extends AbstractResourceType
{
    public function __construct(
        string $dataClass,
        private readonly SliderRepository $sliderRepository,
    ) {
        parent::__construct($dataClass);
    }

    /**
     * @throws NonUniqueResultException
     */
    public function buildForm(FormBuilderInterface $builder, array $options): void
    {
        $builder
            ->add('translations', ResourceTranslationsType::class, [
                'entry_type' => SliderTranslationType::class,
                'label' => 'sylius.form.slider.translations',
            ])
            ->add('position', HiddenType::class, [
                'data' => $this->getPosition(),
            ])
        ;
    }

    public function getBlockPrefix(): string
    {
        return 'app_slider';
    }

    /**
     * @throws NonUniqueResultException
     * @throws NoResultException
     */
    private function getPosition(): int
    {
        $position = $this->sliderRepository->getLastPosition();

        return $position !== null ? $position++ : 1;
    }
}

sylius_resource.yaml配置

# @see https://github.com/Sylius/SyliusResourceBundle/blob/master/docs/index.md
sylius_resource:
    # Override default settings
    #settings:

    # Configure the mapping for your resources
    mapping:
        paths:
            - '%kernel.project_dir%/src/Entity'

    # Configure your resources
    resources:
        app.slider:
            driver: doctrine/orm
            classes:
                model: App\Entity\Slider\Slider
                interface: App\Entity\Slider\SliderInterface
                form: App\Form\Type\Slider\SliderType
            translation:
                classes:
                    model: App\Entity\Slider\SliderTranslation
                    interface: App\Entity\Slider\SliderTranslationInterface
                    form: App\Form\Type\Slider\SliderTranslationType
                    controller: Sylius\Bundle\ResourceBundle\Controller\ResourceController

翻译模板Twig代码

{% import '@SyliusAdmin/shared/helper/translations.html.twig' as translations %}

{% set form = hookable_metadata.context.form %}
{% set prefixes = hookable_metadata.prefixes %}

<div class="tab-pane {% if hookable_metadata.configuration.active|default(false) %}show active{% endif %}" id="slider-translations" role="tabpanel" tabindex="0">
    <div class="card mb-3">
        <div class="card-header">
            <h2 class="card-title">
                {{ 'sylius.ui.translations'|trans }}
            </h2>
        </div>
        <div>
            {{ translations.with_hook(form.translations, prefixes, null, {accordion_id: 'slider-translations', accordion_flush: true}) }}
        </div>
    </div>
</div>

Link字段输入模板Twig代码

{% set form = hookable_metadata.context.form %}
{% set locale = hookable_metadata.context.locale %}

<div class="col-12">
    {{ form_row(form.link) }}
</div>

可能的原因及解决办法

  • ORM映射关联冲突:SliderTranslation继承的AbstractTranslation已自带translatable字段映射,无需手动在实体中定义关联。检查数据库表结构,确保app_slider_translation表的translatable_id和locale字段正确关联到app_slider表,且无重复映射导致的查询异常。
  • 表单数据访问权限:Slider实体的translations属性为protected,需确保Sylius的表单组件能通过TranslatableTrait提供的方法正确访问翻译集合。可临时将属性改为public测试,确认是否是权限问题。
  • Locale上下文不匹配:编辑页面加载时,当前请求的locale需与数据库中存储的翻译locale一致。在getTranslation方法中打印当前locale,确认是否获取到对应locale的翻译实体;同时检查后台用户的默认locale设置。
  • 数据库数据缺失:直接查询app_slider_translation表,确认对应Slider记录的link字段有值,且locale字段与当前请求locale匹配。若不存在对应locale的翻译记录,Sylius会自动创建空实体,导致link字段为空。
  • 模板表单传递错误:在link字段模板中添加{{ dump(form) }},确认hookable_metadata.context.form是SliderTranslation的表单实例,且表单数据中包含link的有效值。

内容的提问来源于stack exchange,提问作者Vladan Zivanovic

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.13 04:15:54