From 51d6bed43decdd9661764e7bbf1bda1c8c12dc8e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Tue, 25 Nov 2014 14:49:15 +0100 Subject: [PATCH] add search possibility throught all bundles, and remove deps between mainbundle and person bundle. refs #223 --- ChillMainBundle.php | 7 ++ Controller/SearchController.php | 48 ++++++++++++ .../SearchableServicesCompilerPass.php | 73 ++++++++++++++++++ Resources/config/routing.yml | 4 + Resources/config/services.yml | 4 + Resources/translations/messages.fr.yml | 2 +- Resources/views/Search/list.html.twig | 25 ++++++ Resources/views/layout.html.twig | 4 +- Search/AbstractSearch.php | 38 ++++++++++ Search/SearchInterface.php | 76 +++++++++++++++++++ Search/SearchProvider.php | 58 ++++++++++++++ 11 files changed, 336 insertions(+), 3 deletions(-) create mode 100644 Controller/SearchController.php create mode 100644 DependencyInjection/SearchableServicesCompilerPass.php create mode 100644 Resources/views/Search/list.html.twig create mode 100644 Search/AbstractSearch.php create mode 100644 Search/SearchInterface.php create mode 100644 Search/SearchProvider.php diff --git a/ChillMainBundle.php b/ChillMainBundle.php index f634c436b..ba3afd7af 100644 --- a/ChillMainBundle.php +++ b/ChillMainBundle.php @@ -3,7 +3,14 @@ namespace Chill\MainBundle; use Symfony\Component\HttpKernel\Bundle\Bundle; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Chill\MainBundle\DependencyInjection\SearchableServicesCompilerPass; class ChillMainBundle extends Bundle { + public function build(ContainerBuilder $container) + { + parent::build($container); + $container->addCompilerPass(new SearchableServicesCompilerPass()); + } } diff --git a/Controller/SearchController.php b/Controller/SearchController.php new file mode 100644 index 000000000..280292082 --- /dev/null +++ b/Controller/SearchController.php @@ -0,0 +1,48 @@ + + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +namespace Chill\MainBundle\Controller; + +use Symfony\Bundle\FrameworkBundle\Controller\Controller; +use Symfony\Component\HttpFoundation\Request; + +/** + * + * + * @author julien.fastre@champs-libres.coop + * @author marc@champs-libres.coop + */ + +class SearchController extends Controller +{ + public function searchAction(Request $request) + { + $searches = $this->get('chill.main.search_provider')->getByOrder(); + $results = array(); + + foreach ($searches as $search ) { + $results[] = $search->renderResult($request->get('q')); + } + + return $this->render('ChillMainBundle:Search:list.html.twig', array('results' => $results) ); + } +} \ No newline at end of file diff --git a/DependencyInjection/SearchableServicesCompilerPass.php b/DependencyInjection/SearchableServicesCompilerPass.php new file mode 100644 index 000000000..ed2903cc5 --- /dev/null +++ b/DependencyInjection/SearchableServicesCompilerPass.php @@ -0,0 +1,73 @@ + + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +namespace Chill\MainBundle\DependencyInjection; + +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; +use Symfony\Component\DependencyInjection\Reference; + +class SearchableServicesCompilerPass implements CompilerPassInterface +{ + + /* + * (non-PHPdoc) + * @see \Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface::process() + */ + public function process(ContainerBuilder $container) + { + if (!$container->hasDefinition('chill.main.search_provider')) { + throw new \LogicException('service chill.main.search_provider ' + . 'is not defined.'); + } + + $definition = $container->getDefinition( + 'chill.main.search_provider' + ); + + $taggedServices = $container->findTaggedServiceIds( + 'chill.search' + ); + + $knownAliases = array(); + + foreach ($taggedServices as $id => $tagAttributes) { + + foreach ($tagAttributes as $attributes) { + + if (!isset($attributes["alias"])) { + throw new \LogicException("the 'name' attribute is missing in your ". + "service '$id' definition"); + } + + if (array_search($attributes["alias"], $knownAliases)) { + throw new \LogicException("There is already a chill.search service with alias " + .$attributes["alias"].". Choose another alias."); + } + $knownAliases[] = $attributes["alias"]; + + $definition->addMethodCall( + 'addSearchService', + array(new Reference($id), $attributes["alias"]) + ); + } + } + } +} \ No newline at end of file diff --git a/Resources/config/routing.yml b/Resources/config/routing.yml index eb9979aa5..331f4ae21 100644 --- a/Resources/config/routing.yml +++ b/Resources/config/routing.yml @@ -28,6 +28,10 @@ chill_main_admin_central: order: 20 label: Admin Menu icons: [gears] + +chill_main_search: + pattern: /{_locale}/person/search + defaults: { _controller: ChillMainBundle:Search:search } login: path: /login diff --git a/Resources/config/services.yml b/Resources/config/services.yml index e6035a508..93cd62958 100644 --- a/Resources/config/services.yml +++ b/Resources/config/services.yml @@ -54,3 +54,7 @@ services: - "@request_stack" tags: - { name: form.type, alias: select2_chill_country } + + chill.main.search_provider: + class: Chill\MainBundle\Search\SearchProvider + diff --git a/Resources/translations/messages.fr.yml b/Resources/translations/messages.fr.yml index 3a016743d..915538c9f 100644 --- a/Resources/translations/messages.fr.yml +++ b/Resources/translations/messages.fr.yml @@ -1,4 +1,4 @@ -Search a person: Rechercher une personne +Search: Rechercher Person name: Nom / Prénom de la personne Login: Connexion Logout: Se déconnecter diff --git a/Resources/views/Search/list.html.twig b/Resources/views/Search/list.html.twig new file mode 100644 index 000000000..0f633263f --- /dev/null +++ b/Resources/views/Search/list.html.twig @@ -0,0 +1,25 @@ +{# + * Copyright (C) 2014, Champs Libres Cooperative SCRLFS, + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . +#} +{% extends "ChillMainBundle::layout.html.twig" %} + +{% block content %} + +{% for result in results %} +{{ result|raw }} +{% endfor %} + +{% endblock %} \ No newline at end of file diff --git a/Resources/views/layout.html.twig b/Resources/views/layout.html.twig index f0fd78dba..66c6c41a7 100644 --- a/Resources/views/layout.html.twig +++ b/Resources/views/layout.html.twig @@ -32,8 +32,8 @@ diff --git a/Search/AbstractSearch.php b/Search/AbstractSearch.php new file mode 100644 index 000000000..b39b20dc8 --- /dev/null +++ b/Search/AbstractSearch.php @@ -0,0 +1,38 @@ + + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +namespace Chill\MainBundle\Search; + +use Chill\MainBundle\Search\SearchInterface; + +/** + * This class implements abstract search with most common responses. + * + * you should use this abstract class instead of SearchInterface : if the signature of + * search interface change, the generic method will be implemented here. + * + * @author Julien Fastré + * + */ +abstract class AbstractSearch implements SearchInterface +{ + +} \ No newline at end of file diff --git a/Search/SearchInterface.php b/Search/SearchInterface.php new file mode 100644 index 000000000..c102f229e --- /dev/null +++ b/Search/SearchInterface.php @@ -0,0 +1,76 @@ + + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +namespace Chill\MainBundle\Search; + +/** + * This interface must be implemented on services which provide search results. + * + * @todo : write doc and add a link to documentation + * + * @author Julien Fastré + * + */ +interface SearchInterface #-> good name ? +{ + /* + * return the result in a html string. The string will be inclued (as raw) + * into a global view. + * + * The global view may be : + * {% for result as resultsFromDifferentSearchInterface %} + * {{ result|raw }} + * {% endfor %} + * + * @param string $pattern the string to search + * @param int $start the first result (for pagination) + * @param int $limit the number of result (for pagination) + * @param array $option the options, specific for each search + * @return string, an HTML string + */ + public function renderResult($pattern, $start=0, $limit=50, array $options = array()); + + /* + * we may desactive the search interface by default. in this case, + * the search will be launch and rendered only with "advanced search" + * + * this may be activated/desactived from bundle definition in config.yml + * + * @return boolean + */ + public function isActiveByDefault(); + + /* + * a string used in advanced search to activate the search + * + * @return string a string which will be translated by twig + */ + public function getLabel(); + + /* + * the order in which the results will appears in the global view + * + * (this may be eventually defined in config.yml) + * + * @return int + */ + public function getOrder(); +} diff --git a/Search/SearchProvider.php b/Search/SearchProvider.php new file mode 100644 index 000000000..fcf24dd06 --- /dev/null +++ b/Search/SearchProvider.php @@ -0,0 +1,58 @@ +get('chill.main.search_provider') + */ +class SearchProvider +{ + /** + * + * @var SearchInterface[] + */ + private $searchServices = array(); + + /* + * return search services in an array, ordered by + * the order key (defined in service definition) + * the conflicts in keys (twice the same order) are resolved + * within the compiler : the function will preserve all services + * defined (if two services have the same order, the will increment + * the order of the second one. + * + * @return SearchInterface[], with an int as array key + */ + public function getByOrder() + { + //sort the array + uasort($this->searchServices, function(SearchInterface $a, SearchInterface $b) { + if ($a->getOrder() == $b->getOrder()) { + return 0; + } + return ($a->getOrder() < $b->getOrder()) ? -1 : 1; + }); + + return $this->searchServices; + } + + /* + * return search services with a specific name, defined in service + * definition. + * + * @return SearchInterface + */ + public function getByName($name) + { + return $this->searchServices[$name]; + } + + public function addSearchService(SearchInterface $service, $name) + { + $this->searchServices[$name] = $service; + } +} \ No newline at end of file