如何在类中获取Symfony仓库?Respect/Validation验证规则问题
我来帮你解决这两个Symfony和Respect/Validation的常见问题:
在Symfony里,最规范也最推荐的方式是依赖注入,别直接从容器里硬拿,这样代码耦合度低,还方便写测试。分几种常见情况给你说明:
控制器中获取:
优先用构造函数注入Repository,Symfony会自动帮你完成实例化;也可以用框架自带的getDoctrine方法快速获取。比如构造注入的写法:use App\Repository\CountryRepository; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; class CountryController extends AbstractController { private $countryRepo; public function __construct(CountryRepository $countryRepo) { $this->countryRepo = $countryRepo; } public function list() { $countries = $this->countryRepo->findAll(); // 后续业务逻辑 } }自定义服务类(比如你的验证规则类):
只要你的类在src/目录下,Symfony默认会把它注册为服务,直接在构造函数里添加Repository参数即可,框架会自动注入:use App\Repository\CountryRepository; class YourCustomService { private $countryRepo; public function __construct(CountryRepository $countryRepo) { $this->countryRepo = $countryRepo; } // 业务逻辑中直接使用 $this->countryRepo }特殊场景下从容器获取(不推荐):
若遇到老代码或特殊需求必须从容器获取,可实现ContainerAwareInterface,但这种写法会让类与容器耦合,能不用就不用:use Symfony\Component\DependencyInjection\ContainerAwareInterface; use Symfony\Component\DependencyInjection\ContainerAwareTrait; class LegacyClass implements ContainerAwareInterface { use ContainerAwareTrait; public function someOldMethod() { $countryRepo = $this->container->get(CountryRepository::class); } }
你的CountryNameUnique需要访问数据库,核心是把Repository注入到这个规则类里——因为AbstractRule本身不支持依赖注入,咱们得修改构造函数来接收Repository,再编写验证逻辑:
首先改造CountryNameUnique类:
use App\Repository\CountryRepository; use Respect\Validation\Rules\AbstractRule; class CountryNameUnique extends AbstractRule { private $countryRepository; // 通过构造函数注入Repository public function __construct(CountryRepository $countryRepository) { $this->countryRepository = $countryRepository; } public function validate($input) { // 假设你的Country实体有name字段,Repository包含findOneByName方法 $existingCountry = $this->countryRepository->findOneByName($input); // 验证逻辑:数据库无同名则返回true(通过),有则返回false(失败) return !$existingCountry; } }
接着调整CountryName规则类,因为现在CountryNameUnique需要构造参数,咱们要改它的构造函数,直接注入实例化好的CountryNameUnique:
use Respect\Validation\Rules\AllOf; use Respect\Validation\Rules\Alpha; use Respect\Validation\Rules\Length; use Respect\Validation\Rules\NotEmpty; use Respect\Validation\Rules\StringType; class CountryName extends AllOf { public function __construct(CountryNameUnique $countryNameUnique) { parent::__construct( new StringType(), new NotEmpty(), new Alpha(), new Length(1, 100), $countryNameUnique // 使用注入好的实例 ); } }
在Symfony中,只要你的类在src/目录下,自动装配(autowire)会自动完成依赖注入:把CountryRepository注入到CountryNameUnique,再把CountryNameUnique注入到CountryName,无需额外配置。
比如在控制器中使用这个验证规则:
use App\Validation\CountryName; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\HttpFoundation\Request; class CountryController extends AbstractController { public function create(Request $request, CountryName $countryNameValidator) { $countryName = $request->request->get('name'); if (!$countryNameValidator->validate($countryName)) { // 验证失败,获取错误信息 $errors = $countryNameValidator->assert($countryName); // 处理错误,比如返回表单提示 } // 验证通过,继续创建国家的逻辑 } }
如果需要支持编辑场景(允许当前国家名称不变,仅检查其他国家的名称唯一性),可以给CountryNameUnique添加排除ID的参数:
class CountryNameUnique extends AbstractRule { private $countryRepository; private $excludeId; // 新增excludeId参数,默认null表示不排除 public function __construct(CountryRepository $countryRepository, int $excludeId = null) { $this->countryRepository = $countryRepository; $this->excludeId = $excludeId; } public function validate($input) { $queryBuilder = $this->countryRepository->createQueryBuilder('c') ->where('c.name = :name') ->setParameter('name', $input); // 若有排除ID,添加条件排除当前国家 if ($this->excludeId !== null) { $queryBuilder->andWhere('c.id != :id') ->setParameter('id', $this->excludeId); } $existingCountry = $queryBuilder->getQuery()->getOneOrNullResult(); return !$existingCountry; } }
编辑时可以这样实例化规则:
// 编辑ID为5的国家,排除该ID不检查 $countryNameUnique = new CountryNameUnique($countryRepo, 5); $countryNameValidator = new CountryName($countryNameUnique);
内容的提问来源于stack exchange,提问作者akor

