Помогите разобраться с необходимой структурой кода. Есть четкое понимание, что сделано у меня... криво. Работает, но код громоздкий какой-то, повторяется.
У меня много сущностей с взаимосвязями. Чтобы управлять этими связями в админке я использую
tetranz/select2entity-bundle. Соответственно в классе формы, поле со связью описано с указанием типа Select2EntityType. Одним из параметров у этого типа поля, является remote_route, который задает роут для обработки Ajax запроса, который возвращает список потенциальных сущностей для создания связи.
Дальше я приведу пример конкретной связи и код. Есть такие сущности, как Category (категория), Tag (тег) и Faq (вопрос/ответ). И Категория и Вопросы имеют связь с сущьностью Тег. При создании/редактировании связи c сущностью Tag поиск идет по полю имени сущности Tag (по имени тега).
Код формы CategoryType и FaqType, а так же контроллера, который и обрабатывает Ajax запрос от всех сущностей которые имеют связь с тегом (их больше, не только Category и Faq):
class CategoryType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
->add('tags', Select2EntityType::class, [
'multiple' => true,
'remote_route' => 'admin_tag_ajax_searching',
'class' => Tag::class,
'remote_params' => [
'entityClass' => urlencode(Category::class),
'entityId' => $options['data']->getId(),
],
]);
}
}
class FaqType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
->add('tags', Select2EntityType::class, [
'multiple' => true,
'remote_route' => 'admin_tag_ajax_searching',
'class' => Tag::class,
'remote_params' => [
'entityClass' => urlencode(Faq::class),
'entityId' => $options['data']->getId(),
],
]);
}
}
class TagController extends Controller
{
/**
* @Route("/tag_ajax_searching", methods={"GET"}, name="admin_tag_ajax_searching")
*/
public function search(Request $request, TagRepository $tagRepository): Response
{
$query = $request->query->get('q', '');
$limit = $request->query->get('limit', 20);
$entityId = $request->query->get('entityId');
$entityClass = urldecode($request->query->get('entityClass'));
$conditions = [];
// исключить из поиска уже назначенные Теги
if ($entityId) {
$entityRepository = $this->getDoctrine()->getRepository($entityClass);
$entity = $entityRepository->findOneBy(['id' => $entityId]);
/** @var ArrayCollection|Tag[] */
$conditions['excludeTags'] = $entity->getTags();
}
$foundTags = $tagRepository->findBySearchQuery($query, $limit, $conditions);
$results = [];
foreach ($foundTags as $tag) {
$results[] = [
'id' => htmlspecialchars($tag->getId()),
'text' => htmlspecialchars($tag->getTitle()),
];
}
return $this->json($results);
}
}
Собственно вопросы такие. Задам пунктами, чтобы четче сформулировать беспокоящие моменты. Первый сформулировать просто
- Вопрос 1. Сущностей, с которыми Tag имеет связь - несколько (category, faq, article и т.д.), и все эти сущности делают запрос к одному методу контроллера TagController::search(). Правильно ли это? Я думаю да, но есть следующий момент...
Мне нужно отдать список Тегов исключив из него те, которые уже назначены у данной сущности, а для этого мне нужно знать, что за сущность редактируется (ее класс) и какой у нее id.
Например, редактируем список тегов у категории. Для этого я передаю с AJAX запросом 2 дополнительных параметра (в коде обеих форм это видно): entityClass и entityId.
'remote_params' => [
'entityClass' => urlencode(Faq::class),
'entityId' => $options['data']->getId(),
]
Дальше я получаю имя класса Entity в контроллере:
$entityClass = urldecode($request->query->get('entityClass'));
И получаю соответствующий репозиторий:
$entityRepository = $this->getDoctrine()->getRepository($entityClass);
- Вопрос 2. Правильно ли так передавать и получать имя класса Entity в моем случае? Я же не могу подгружать (инектить в контроллер) сразу все репозитории, поэтому определяю нужный репозиторий по имени класса.
Дальше уже делаю то, ради чего все делалось - получаю редактируемую категорию (категорию потому что пример с категорией, хотя там может быть и другая сущность) и достаю назначенные ей теги:
$entity = $entityRepository->findOneBy(['id' => $entityId]);
/** @var ArrayCollection|Tag[] */
$conditions['excludeTags'] = $entity->getTags();
Дальше $conditions, вместе со строкой поиска, передается в репозиторий тегов для выборки тегов.
$foundTags = $tagRepository->findBySearchQuery($query, $limit, $conditions);
Весь код метода, который выполняет генерацию запроса к БД приводить не буду, только важную часть - эта часть как раз добавляет в запрос к БД условие 'NOT IN(id, id, id, id)':
if (isset($conditions['excludeTags'])) {
/** @var $tag Tag */
foreach ($conditions['excludeTags'] as $tag) {
$ids[] = $tag->getId();
}
if (isset($ids)) {
$qb->andWhere($qb->expr()->notIn('tag.id', $ids));
}
}
Понимаю, что конец данного поста сумбурный вышел, но я старался как мог объяснить то, что меня смущает в данной ситуации. А реальность такова, что подобный метод контроллера, с роутом типа "admin_ХХХ_ajax_searching" есть практически в каждом контроллере - article, category, gallery, tag и будут еще другие, уверен. И все они однотипны, все смущают теми вопросами, что я описал выше. Хочу переписать, но не знаю с чего начать, и как правильнее.
Вообще, главный вопрос, обобщающий так сказать, задан в теме топика - "Как реализовать обработку AJAX запроса от разных сущностей к одному контроллеру?". Возможно то, как я это сделал - вообще в корне неверно...
Помогите пожалуйста :)
Спасибо!