Skip to content

Instantly share code, notes, and snippets.

@hiro-iFactory
Last active August 29, 2015 14:05
Show Gist options
  • Save hiro-iFactory/178aa315788311102375 to your computer and use it in GitHub Desktop.
Save hiro-iFactory/178aa315788311102375 to your computer and use it in GitHub Desktop.
Example of how not to use Oro Search Bundle for Auto-completion field (Data Flow: services.yml > MyFormType.php > AutocompleteController.php > Helper.php)
<?php
// src/Acme/Bundle/AcmeBundle/Controller/AutocompleteController.php
namespace Acme\Bundle\AcmeBundle\Controller;
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
use Symfony\Component\HttpKernel\Exception\HttpException;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Oro\Bundle\SecurityBundle\Annotation\AclAncestor;
use Oro\Bundle\FormBundle\Autocomplete\Security;
use Oro\Bundle\FormBundle\Autocomplete\SearchHandlerInterface;
use Acme\Bundle\AcmeBundle\Helper\Helper;
/**
* @Route("/acme-autocomplete")
*/
class AutocompleteController extends Controller
{
/* @var string */
private $name;
/* @var string */
private $query;
/* @var integer */
private $page;
/* @var integer */
private $perPage;
/* @var boolean */
private $searchById;
private function validate(Request $request)
{
$this->name = $request->get('name');
$this->query = $request->get('query');
$this->page = intval($request->get('page', 1));
$this->perPage = intval($request->get('per_page', 50));
$this->searchById = (bool) $request->get('search_by_id', false);
if ( ! $this->name) {
throw new HttpException(400, 'Parameter "name" is required');
}
if ($this->page <= 0) {
throw new HttpException(400, 'Parameter "page" must be greater than 0');
}
if ($this->perPage <= 0) {
throw new HttpException(400, 'Parameter "per_page" must be greater than 0');
}
if ( ! $this->get('oro_form.autocomplete.security')->isAutocompleteGranted($this->name)) {
throw new AccessDeniedHttpException('Access denied.');
}
}
/**
* @Route("/category", name="acme_form_autocomplete")
* AclAncestor("acme_category_view")
*/
public function searchCategoryAction(Request $request)
{
$this->validate($request);
/** @var Helper $helper */
$helper = $this->get('acme.helper');
return new JsonResponse($helper->getMyCategories($this->query, $this->page, $this->perPage, $this->searchById));
}
}
<?php
// src/Acme/Bundle/AcmeBundle/Helper/Helper.php
namespace Acme\Bundle\AcmeBundle\Helper;
use Symfony\Component\DependencyInjection\ContainerInterface as Container;
use Doctrine\ORM\EntityRepository;
use Doctrine\ORM\Query;
use Doctrine\ORM\QueryBuilder;
use Doctrine\ORM\Query\Parameter;
use Doctrine\ORM\Query\Expr;
use Doctrine\ORM\Query\Expr\Andx;
use Oro\Bundle\UserBundle\Entity\User;
use Acme\Bundle\AcmeBundle\Entity\Acme;
class Helper
{
private $container;
public function __construct(Container $container) {
$this->container = $container;
}
/**
* Generate data for Autocompletion request
*
* Usage 1:
* return $this->arrayAutoComplete($page, $perPage, $query);
*
* Usage 2:
* return $this->arrayAutoComplete($page, $perPage, $query, function($results) {
* $result = array();
* foreach ($results as $data) {
* $result[] = array(
* 'id' => $data['id'],
* 'name' => $data['code'] . ': ' . $data['name'],
* );
* }
* return $result;
* });
*
* @param integer $page
* @param integer $perPage
* @param QueryBuilder $query
* @param callable $func
*
* @return array
*/
public function arrayAutoComplete($page, $perPage, $query, callable $func = null) {
$page = (int) $page > 0 ? (int) $page : 1;
$perPage = (int) $perPage > 0 ? (int) $perPage : 10;
$firstResult = ($page - 1) * $perPage;
$query->setMaxResults($perPage);
$query->setFirstResult($firstResult);
// Convert Parameters to simple array to debug easier
$params = $query->getParameters();
$paramsDebug = array();
foreach ($params as $p) {
$paramsDebug[$p->getName()] = $p->getValue();
}
// var_dump(array('DQL' => $query->getDQL(), 'Params' => $paramsDebug));
$results = $query->getQuery()->getArrayResult();
$more = count($results) == $perPage;
if ($more) {
$results = array_slice($results, 0, $perPage);
}
if (is_null($func)) {
$result = array();
foreach ($results as $data) {
$result[] = array(
'id' => $data['id'],
'name' => $data['name'],
);
}
} else {
$result = $func($results);
}
/*
return array(
'results' => $result,
'more' => $more,
'dql' => $query->getDQL(),
'max_result' => $perPage,
'first_result' => $firstResult,
'params' => $paramsDebug,
);
*/
return array(
'results' => $result,
'more' => $more,
);
}
/**
* Get Category List for Current User
*/
public function getMyCategories($queryString, $page, $perPage, $searchById) {
$doctrine = $this->getDoctrine();
/* @var User $user */
$user = $this->getUser();
/* @var EntityRepository $repository */
$repository = $doctrine->getRepository('AcmeBundle:Acme');
/* @var QueryBuilder $query */
$query = $repository->createQueryBuilder('a');
// Your custom logic here
// $query->xxxx();
return $this->arrayAutoComplete($page, $perPage, $query);
}
/**
* Dump data with \Doctrine\Common\Util\Debug::dump()
*
* @param data
*/
function dump($var, $maxDepth = 2, $stripTags = true) {
\Doctrine\Common\Util\Debug::dump($var, $maxDepth, $stripTags);
}
/**
* The following methods are copied from Symfony\Bundle\FrameworkBundle\Controller\Controller
*/
public function getRequest()
{
return $this->container->get('request');
}
public function getDoctrine()
{
if (!$this->container->has('doctrine')) {
throw new \LogicException('The DoctrineBundle is not registered in your application.');
}
return $this->container->get('doctrine');
}
public function getUser()
{
if (!$this->container->has('security.context')) {
throw new \LogicException('The SecurityBundle is not registered in your application.');
}
if (null === $token = $this->container->get('security.context')->getToken()) {
return null;
}
if (!is_object($user = $token->getUser())) {
return null;
}
return $user;
}
public function has($id)
{
return $this->container->has($id);
}
public function get($id)
{
return $this->container->get($id);
}
}
<?php
// src/Acme/Bundle/AcmeBundle/Form/Type/MyFormType.php
namespace Acme\Bundle\AcmeBundle\Form\Type;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
use Atf\Bundle\QuotesBundle\Form\EventListener\QuoteSubscriber;
use Atf\Bundle\QuotesBundle\Helper\Helper;
class MyFormType extends AbstractType
{
/**
* @param FormBuilderInterface $builder
* @param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add(
'category',
'oro_jqueryselect2_hidden',
array(
'autocomplete_alias' => 'acme_autocomplete',
'required' => false,
'configs' => array(
'placeholder' => 'Select Category (as Category is example field here)',
/**
* "acme_form_autocomplete" is in:
* Acme\Bundle\AcmeBundle\Controller\AutocompleteController
*/
'route_name' => 'acme_form_autocomplete',
),
'label' => 'Category'
)
);
#
# src/Acme/Bundle/AcmeBundle/Resources/config/services.yml
#
parameters:
acme.helper.class: Acme\Bundle\AcmeBundle\Helper\Helper
acme.entity.class: Acme\Bundle\AcmeBundle\Entity\Acme
services:
#
# Helper class to do the custom business logic
#
acme.helper:
class: %acme.helper.class%
arguments: ['@service_container']
#
# Search Handler for Autocomplete
#
# Re-declaring the containier class "oro_form.autocomplete.search_handler" without "calls" parameter
# as new container service so that the Search Bundle related logic is not called
# and that let us avoid having to create the file "search.yml" and its associated data for search fuctionality
# (which I don't need)
#
# (The original container class "oro_form.autocomplete.search_handler" is found at:
# vendor/oro/platform/src/Oro/Bundle/FormBundle/Resources/config/autocomplete.yml)
#
acme_form.autocomplete.search_handler:
class: %oro_form.autocomplete.search_handler.class%
abstract: true
#
# Specify this service in Form Type Class
#
acme.form.autocomplete.search_handler:
parent: acme_form.autocomplete.search_handler
arguments:
- %acme.entity.class%
- [name]
tags:
- { name: oro_form.autocomplete.search_handler, alias: acme_autocomplete, acl_resource: acme_view }
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment