User search refactored, autocomplete added. bootstrap3-typeahead jQuery plugin added.
This commit is contained in:
parent
d71ea8a116
commit
d0c103eae0
|
@ -35,6 +35,9 @@ sensio_framework_extra:
|
||||||
|
|
||||||
# Twig Configuration
|
# Twig Configuration
|
||||||
twig:
|
twig:
|
||||||
|
form:
|
||||||
|
resources:
|
||||||
|
- bootstrap_3_layout.html.twig
|
||||||
debug: "%kernel.debug%"
|
debug: "%kernel.debug%"
|
||||||
strict_variables: "%kernel.debug%"
|
strict_variables: "%kernel.debug%"
|
||||||
|
|
||||||
|
|
|
@ -3,17 +3,42 @@
|
||||||
namespace Skobkin\Bundle\PointToolsBundle\Controller;
|
namespace Skobkin\Bundle\PointToolsBundle\Controller;
|
||||||
|
|
||||||
use Doctrine\ORM\EntityManager;
|
use Doctrine\ORM\EntityManager;
|
||||||
use Doctrine\ORM\QueryBuilder;
|
use Skobkin\Bundle\PointToolsBundle\Form\UserSearchType;
|
||||||
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
|
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
|
||||||
|
use Symfony\Component\Form\FormError;
|
||||||
|
use Symfony\Component\HttpFoundation\JsonResponse;
|
||||||
|
use Symfony\Component\HttpFoundation\Request;
|
||||||
|
|
||||||
class MainController extends Controller
|
class MainController extends Controller
|
||||||
{
|
{
|
||||||
public function indexAction()
|
public function indexAction(Request $request)
|
||||||
{
|
{
|
||||||
/** @var EntityManager $em */
|
/** @var EntityManager $em */
|
||||||
$em = $this->getDoctrine()->getManager();
|
$em = $this->getDoctrine()->getManager();
|
||||||
|
|
||||||
|
$form = $this->createForm(
|
||||||
|
new UserSearchType(),
|
||||||
|
null,
|
||||||
|
[
|
||||||
|
'action' => $this->generateUrl('index'),
|
||||||
|
'method' => 'POST',
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
$form->handleRequest($request);
|
||||||
|
|
||||||
|
if ($form->isSubmitted() && $form->isValid()) {
|
||||||
|
$login = $form->get('login')->getData();
|
||||||
|
|
||||||
|
if (null !== $user = $em->getRepository('SkobkinPointToolsBundle:User')->findOneBy(['login' => $login])) {
|
||||||
|
return $this->redirectToRoute('user_show', ['login' => $login]);
|
||||||
|
}
|
||||||
|
|
||||||
|
$form->get('login')->addError(new FormError('Login not found'));
|
||||||
|
}
|
||||||
|
|
||||||
return $this->render('SkobkinPointToolsBundle:Main:index.html.twig', [
|
return $this->render('SkobkinPointToolsBundle:Main:index.html.twig', [
|
||||||
|
'form' => $form->createView(),
|
||||||
'users_count' => $em->getRepository('SkobkinPointToolsBundle:User')->getUsersCount(),
|
'users_count' => $em->getRepository('SkobkinPointToolsBundle:User')->getUsersCount(),
|
||||||
'subscribers_count' => $em->getRepository('SkobkinPointToolsBundle:Subscription')->getUserSubscribersCountById($this->container->getParameter('point_id')),
|
'subscribers_count' => $em->getRepository('SkobkinPointToolsBundle:Subscription')->getUserSubscribersCountById($this->container->getParameter('point_id')),
|
||||||
'events_count' => $em->getRepository('SkobkinPointToolsBundle:SubscriptionEvent')->getLastDayEventsCount(),
|
'events_count' => $em->getRepository('SkobkinPointToolsBundle:SubscriptionEvent')->getLastDayEventsCount(),
|
||||||
|
@ -21,4 +46,20 @@ class MainController extends Controller
|
||||||
'last_events' => $em->getRepository('SkobkinPointToolsBundle:SubscriptionEvent')->getLastSubscriptionEvents(10),
|
'last_events' => $em->getRepository('SkobkinPointToolsBundle:SubscriptionEvent')->getLastSubscriptionEvents(10),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function searchUserAjaxAction($login)
|
||||||
|
{
|
||||||
|
$em = $this->getDoctrine()->getManager();
|
||||||
|
|
||||||
|
$result = [];
|
||||||
|
|
||||||
|
foreach ($em->getRepository('SkobkinPointToolsBundle:User')->findUsersLikeLogin($login) as $user) {
|
||||||
|
$result[] = [
|
||||||
|
'login' => $user->getLogin(),
|
||||||
|
'name' => $user->getName(),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
return new JsonResponse($result);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -50,19 +50,6 @@ class UserController extends Controller
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @param Request $request
|
|
||||||
*/
|
|
||||||
public function searchUserAction(Request $request)
|
|
||||||
{
|
|
||||||
$login = $request->request->get('login');
|
|
||||||
|
|
||||||
if (!$login) {
|
|
||||||
return $this->redirectToRoute('index');
|
|
||||||
}
|
|
||||||
return $this->redirectToRoute('user_show', ['login' => $login]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param TopUserDTO[] $topUsers
|
* @param TopUserDTO[] $topUsers
|
||||||
* @return Highchart
|
* @return Highchart
|
||||||
|
|
39
src/Skobkin/Bundle/PointToolsBundle/Form/UserSearchType.php
Normal file
39
src/Skobkin/Bundle/PointToolsBundle/Form/UserSearchType.php
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Skobkin\Bundle\PointToolsBundle\Form;
|
||||||
|
|
||||||
|
use Symfony\Component\Form\AbstractType;
|
||||||
|
use Symfony\Component\Form\FormBuilderInterface;
|
||||||
|
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||||
|
|
||||||
|
class UserSearchType extends AbstractType
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @param FormBuilderInterface $builder
|
||||||
|
* @param array $options
|
||||||
|
*/
|
||||||
|
public function buildForm(FormBuilderInterface $builder, array $options)
|
||||||
|
{
|
||||||
|
$builder
|
||||||
|
->add('login')
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function configureOptions(OptionsResolver $resolver)
|
||||||
|
{
|
||||||
|
$resolver->setDefaults([
|
||||||
|
'data_class' => 'Skobkin\Bundle\PointToolsBundle\Entity\User',
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getName()
|
||||||
|
{
|
||||||
|
return 'skobkin_bundle_pointtoolsbundle_user_search';
|
||||||
|
}
|
||||||
|
}
|
|
@ -12,7 +12,7 @@ class UserRepository extends EntityRepository
|
||||||
* Case-insensitive user search
|
* Case-insensitive user search
|
||||||
*
|
*
|
||||||
* @param string $login
|
* @param string $login
|
||||||
* @return User[]
|
* @return User|null
|
||||||
* @throws \Doctrine\ORM\NonUniqueResultException
|
* @throws \Doctrine\ORM\NonUniqueResultException
|
||||||
*/
|
*/
|
||||||
public function findUserByLogin($login)
|
public function findUserByLogin($login)
|
||||||
|
@ -28,6 +28,31 @@ class UserRepository extends EntityRepository
|
||||||
;
|
;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Case insensitive user LIKE %login% search
|
||||||
|
*
|
||||||
|
* @param string $login
|
||||||
|
*
|
||||||
|
* @return User[]
|
||||||
|
*/
|
||||||
|
public function findUsersLikeLogin($login)
|
||||||
|
{
|
||||||
|
if (empty($login)) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
$qb = $this->createQueryBuilder('u');
|
||||||
|
|
||||||
|
return $qb
|
||||||
|
->where('u.login LIKE :login')
|
||||||
|
->orderBy('u.login', 'ASC')
|
||||||
|
->setMaxResults(10)
|
||||||
|
->setParameter('login', '%'.strtolower($login).'%')
|
||||||
|
->getQuery()
|
||||||
|
->getResult()
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return integer
|
* @return integer
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -2,10 +2,12 @@ index:
|
||||||
path: /
|
path: /
|
||||||
defaults: { _controller: SkobkinPointToolsBundle:Main:index }
|
defaults: { _controller: SkobkinPointToolsBundle:Main:index }
|
||||||
|
|
||||||
user_search:
|
user_search_ajax:
|
||||||
path: /user/search
|
path: /ajax/users/search/{login}
|
||||||
defaults: { _controller: SkobkinPointToolsBundle:User:searchUser }
|
defaults: { _controller: SkobkinPointToolsBundle:Main:searchUserAjax, _format: json }
|
||||||
methods: [POST]
|
requirements:
|
||||||
|
login: "[\w-]*"
|
||||||
|
_format: json
|
||||||
|
|
||||||
user_show:
|
user_show:
|
||||||
path: /user/{login}
|
path: /user/{login}
|
||||||
|
|
|
@ -1,18 +1,52 @@
|
||||||
{% extends "::base.html.twig" %}
|
{% extends "::base.html.twig" %}
|
||||||
|
|
||||||
|
{% block head_js %}
|
||||||
|
{{ parent() }}
|
||||||
|
<script src="{{ asset('js/bootstrap3-typeahead.min.js') }}"></script>
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="well well-lg">
|
<div class="well well-lg">
|
||||||
{# @todo rewrite to Symfony forms #}
|
{{ form_start(form, {'attr': {'class': 'form-inline'} }) }}
|
||||||
<form class="form-inline" method="post" action="{{ path('user_search') }}">
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="sr-only" for="index-input-username">{{ 'Username'|trans }}</label>
|
{{ form_errors(form.login) }}
|
||||||
<div class="input-group">
|
{{ form_widget(form.login, {'attr': {'autocomplete': 'off'}}) }}
|
||||||
<div class="input-group-addon">@</div>
|
|
||||||
<input name="login" type="text" class="form-control" id="index-input-username" placeholder="username">
|
<script type="text/javascript">
|
||||||
</div>
|
$(function() {
|
||||||
|
$field = $('#{{ form.login.vars.id }}');
|
||||||
|
|
||||||
|
$field.typeahead({
|
||||||
|
minLength: 2,
|
||||||
|
delay: 500,
|
||||||
|
autoSelect: true,
|
||||||
|
source: function (query, processCallback) {
|
||||||
|
$.get('{{ path('user_search_ajax', {'login': ''}) }}' + query, function (data) {
|
||||||
|
processCallback(data);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
afterSelect: function () {
|
||||||
|
$field.parents('form').first().submit();
|
||||||
|
},
|
||||||
|
displayText: function (item) {
|
||||||
|
// Crutches to place only login into the field after selecting the item
|
||||||
|
if (typeof item === 'object') {
|
||||||
|
return item.login+(item.name ? ' ('+item.name+')' : '');
|
||||||
|
} else if (typeof item === 'string') {
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
updater: function (item) {
|
||||||
|
// Crutches to place only login into the field after selecting the item
|
||||||
|
return item.login;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<input type="submit" value="{{ 'Search'|trans }}" class="btn btn-default" />
|
||||||
</div>
|
</div>
|
||||||
<button type="submit" class="btn btn-primary">{{ 'Search'|trans }}</button>
|
{{ form_end(form) }}
|
||||||
</form>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="container service-stats">
|
<div class="container service-stats">
|
||||||
|
|
1
web/js/bootstrap3-typeahead.min.js
vendored
Normal file
1
web/js/bootstrap3-typeahead.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
Loading…
Reference in a new issue