mirror of
https://gitlab.com/Chill-Projet/chill-bundles.git
synced 2025-08-30 19:43:49 +00:00
cs: Fix code style (safe rules only).
This commit is contained in:
@@ -1,98 +1,85 @@
|
||||
<?php
|
||||
|
||||
|
||||
/*
|
||||
/**
|
||||
* Chill is a software for social workers
|
||||
*
|
||||
* Copyright (C) 2014, Champs Libres Cooperative SCRLFS, <http://www.champs-libres.coop>
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Chill\MainBundle\Search;
|
||||
|
||||
use Chill\MainBundle\Search\SearchInterface;
|
||||
use Chill\MainBundle\Search\ParsingException;
|
||||
use DateTime;
|
||||
use function strpos;
|
||||
|
||||
/**
|
||||
* 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é <julien.fastre@champs-libres.coop>
|
||||
*
|
||||
*/
|
||||
abstract class AbstractSearch implements SearchInterface
|
||||
{
|
||||
/**
|
||||
* parse string expected to be a date and transform to a DateTime object
|
||||
* parse string expected to be a date and transform to a DateTime object.
|
||||
*
|
||||
* @param type $string
|
||||
* @return \DateTime
|
||||
*
|
||||
* @throws ParsingException if the date is not parseable
|
||||
*
|
||||
* @return DateTime
|
||||
*/
|
||||
public function parseDate($string)
|
||||
{
|
||||
try {
|
||||
return new \DateTime($string);
|
||||
return new DateTime($string);
|
||||
} catch (ParsingException $ex) {
|
||||
$exception = new ParsingException('The date is '
|
||||
. 'not parsable', 0, $ex);
|
||||
|
||||
throw $exception;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* recompose a pattern, retaining only supported terms
|
||||
* recompose a pattern, retaining only supported terms.
|
||||
*
|
||||
* the outputted string should be used to show users their search
|
||||
*
|
||||
* @param array $terms
|
||||
* @param array $supportedTerms
|
||||
* @param string $domain if your domain is NULL, you should set NULL. You should set used domain instead
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function recomposePattern(array $terms, array $supportedTerms, $domain = NULL)
|
||||
protected function recomposePattern(array $terms, array $supportedTerms, $domain = null)
|
||||
{
|
||||
$recomposed = '';
|
||||
|
||||
if ($domain !== NULL)
|
||||
{
|
||||
$recomposed .= '@'.$domain.' ';
|
||||
if (null !== $domain) {
|
||||
$recomposed .= '@' . $domain . ' ';
|
||||
}
|
||||
|
||||
foreach ($supportedTerms as $term) {
|
||||
if (array_key_exists($term, $terms) && $term !== '_default') {
|
||||
$recomposed .= ' '.$term.':';
|
||||
$containsSpace = \strpos($terms[$term], " ") !== false;
|
||||
if (array_key_exists($term, $terms) && '_default' !== $term) {
|
||||
$recomposed .= ' ' . $term . ':';
|
||||
$containsSpace = strpos($terms[$term], ' ') !== false;
|
||||
|
||||
if ($containsSpace) {
|
||||
$recomposed .= '"';
|
||||
}
|
||||
$recomposed .= (mb_stristr(' ', $terms[$term]) === FALSE) ? $terms[$term] : '('.$terms[$term].')';
|
||||
$recomposed .= (mb_stristr(' ', $terms[$term]) === false) ? $terms[$term] : '(' . $terms[$term] . ')';
|
||||
|
||||
if ($containsSpace) {
|
||||
$recomposed .= '"';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($terms['_default'] !== '') {
|
||||
$recomposed .= ' '.$terms['_default'];
|
||||
if ('' !== $terms['_default']) {
|
||||
$recomposed .= ' ' . $terms['_default'];
|
||||
}
|
||||
|
||||
//strip first character if empty
|
||||
if (mb_strcut($recomposed, 0, 1) === ' '){
|
||||
if (mb_strcut($recomposed, 0, 1) === ' ') {
|
||||
$recomposed = mb_strcut($recomposed, 1);
|
||||
}
|
||||
|
||||
|
@@ -1,59 +1,65 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Chill is a software for social workers
|
||||
*
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Chill\MainBundle\Search\Entity;
|
||||
|
||||
use Chill\MainBundle\Repository\UserRepository;
|
||||
use Chill\MainBundle\Search\SearchApiInterface;
|
||||
use Chill\MainBundle\Search\SearchApiQuery;
|
||||
use function array_map;
|
||||
use function in_array;
|
||||
|
||||
class SearchUserApiProvider implements SearchApiInterface
|
||||
{
|
||||
private UserRepository $userRepository;
|
||||
|
||||
/**
|
||||
* @param UserRepository $userRepository
|
||||
*/
|
||||
public function __construct(UserRepository $userRepository)
|
||||
{
|
||||
$this->userRepository = $userRepository;
|
||||
}
|
||||
|
||||
public function provideQuery(string $pattern, array $parameters): SearchApiQuery
|
||||
{
|
||||
$query = new SearchApiQuery();
|
||||
$query
|
||||
->setSelectKey("user")
|
||||
->setSelectJsonbMetadata("jsonb_build_object('id', u.id)")
|
||||
->setSelectPertinence("GREATEST(SIMILARITY(LOWER(UNACCENT(?)), u.usernamecanonical),
|
||||
SIMILARITY(LOWER(UNACCENT(?)), u.emailcanonical))", [ $pattern, $pattern ])
|
||||
->setFromClause("users AS u")
|
||||
->setWhereClauses("SIMILARITY(LOWER(UNACCENT(?)), u.usernamecanonical) > 0.15
|
||||
OR
|
||||
SIMILARITY(LOWER(UNACCENT(?)), u.emailcanonical) > 0.15
|
||||
", [ $pattern, $pattern ]);
|
||||
|
||||
return $query;
|
||||
}
|
||||
|
||||
public function supportsTypes(string $pattern, array $types, array $parameters): bool
|
||||
{
|
||||
return \in_array('user', $types);
|
||||
}
|
||||
|
||||
public function prepare(array $metadatas): void
|
||||
{
|
||||
$ids = \array_map(fn($m) => $m['id'], $metadatas);
|
||||
|
||||
$this->userRepository->findBy([ 'id' => $ids ]);
|
||||
}
|
||||
|
||||
public function supportsResult(string $key, array $metadatas): bool
|
||||
{
|
||||
return $key === 'user';
|
||||
}
|
||||
|
||||
public function getResult(string $key, array $metadata, float $pertinence)
|
||||
{
|
||||
return $this->userRepository->find($metadata['id']);
|
||||
}
|
||||
|
||||
public function prepare(array $metadatas): void
|
||||
{
|
||||
$ids = array_map(fn ($m) => $m['id'], $metadatas);
|
||||
|
||||
$this->userRepository->findBy(['id' => $ids]);
|
||||
}
|
||||
|
||||
public function provideQuery(string $pattern, array $parameters): SearchApiQuery
|
||||
{
|
||||
$query = new SearchApiQuery();
|
||||
$query
|
||||
->setSelectKey('user')
|
||||
->setSelectJsonbMetadata("jsonb_build_object('id', u.id)")
|
||||
->setSelectPertinence('GREATEST(SIMILARITY(LOWER(UNACCENT(?)), u.usernamecanonical),
|
||||
SIMILARITY(LOWER(UNACCENT(?)), u.emailcanonical))', [$pattern, $pattern])
|
||||
->setFromClause('users AS u')
|
||||
->setWhereClauses('SIMILARITY(LOWER(UNACCENT(?)), u.usernamecanonical) > 0.15
|
||||
OR
|
||||
SIMILARITY(LOWER(UNACCENT(?)), u.emailcanonical) > 0.15
|
||||
', [$pattern, $pattern]);
|
||||
|
||||
return $query;
|
||||
}
|
||||
|
||||
public function supportsResult(string $key, array $metadatas): bool
|
||||
{
|
||||
return 'user' === $key;
|
||||
}
|
||||
|
||||
public function supportsTypes(string $pattern, array $types, array $parameters): bool
|
||||
{
|
||||
return in_array('user', $types);
|
||||
}
|
||||
}
|
||||
|
@@ -1,34 +1,21 @@
|
||||
<?php
|
||||
/*
|
||||
* Copyright (C) 2017 Champs Libres Cooperative <info@champs-libres.coop>
|
||||
|
||||
/**
|
||||
* Chill is a software for social workers
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Chill\MainBundle\Search;
|
||||
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Julien Fastré <julien.fastre@champs-libres.coop>
|
||||
*/
|
||||
interface HasAdvancedSearchFormInterface extends SearchInterface
|
||||
{
|
||||
public function buildForm(FormBuilderInterface $builder);
|
||||
|
||||
|
||||
public function convertFormDataToQuery(array $data);
|
||||
|
||||
public function convertTermsToFormData(array $terms) ;
|
||||
|
||||
|
||||
public function convertTermsToFormData(array $terms);
|
||||
}
|
||||
|
@@ -1,5 +1,12 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Chill is a software for social workers
|
||||
*
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Chill\MainBundle\Search\Model;
|
||||
|
||||
class Result
|
||||
@@ -7,12 +14,11 @@ class Result
|
||||
private float $relevance;
|
||||
|
||||
/**
|
||||
* mixed an arbitrary result
|
||||
* mixed an arbitrary result.
|
||||
*/
|
||||
private $result;
|
||||
|
||||
/**
|
||||
* @param float $relevance
|
||||
* @param $result
|
||||
*/
|
||||
public function __construct(float $relevance, $result)
|
||||
@@ -30,7 +36,4 @@ class Result
|
||||
{
|
||||
return $this->result;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
@@ -1,6 +1,16 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Chill is a software for social workers
|
||||
*
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Chill\MainBundle\Search;
|
||||
|
||||
class ParsingException extends \Exception
|
||||
use Exception;
|
||||
|
||||
class ParsingException extends Exception
|
||||
{
|
||||
}
|
||||
}
|
||||
|
@@ -1,23 +1,30 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Chill is a software for social workers
|
||||
*
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Chill\MainBundle\Search;
|
||||
|
||||
use Chill\MainBundle\Search\Entity\SearchUserApiProvider;
|
||||
use Chill\MainBundle\Serializer\Model\Collection;
|
||||
use Chill\MainBundle\Pagination\PaginatorFactory;
|
||||
use Chill\PersonBundle\Search\SearchPersonApiProvider;
|
||||
use Chill\ThirdPartyBundle\Search\ThirdPartyApiSearch;
|
||||
use Chill\MainBundle\Serializer\Model\Collection;
|
||||
use Doctrine\DBAL\Types\Types;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Doctrine\ORM\Query\ResultSetMappingBuilder;
|
||||
use Chill\MainBundle\Search\SearchProvider;
|
||||
use Symfony\Component\VarDumper\Resources\functions\dump;
|
||||
use function array_map;
|
||||
use function array_merge;
|
||||
use function implode;
|
||||
use function strtr;
|
||||
|
||||
class SearchApi
|
||||
{
|
||||
private EntityManagerInterface $em;
|
||||
|
||||
private PaginatorFactory $paginator;
|
||||
|
||||
private iterable $providers = [];
|
||||
@@ -54,111 +61,25 @@ class SearchApi
|
||||
return new Collection($results, $paginator);
|
||||
}
|
||||
|
||||
private function findQueries($pattern, array $types, array $parameters): array
|
||||
{
|
||||
return \array_map(
|
||||
fn($p) => $p->provideQuery($pattern, $parameters),
|
||||
$this->findProviders($pattern, $types, $parameters),
|
||||
);
|
||||
}
|
||||
|
||||
private function findProviders(string $pattern, array $types, array $parameters): array
|
||||
{
|
||||
$providers = [];
|
||||
|
||||
foreach ($this->providers as $provider) {
|
||||
if ($provider->supportsTypes($pattern, $types, $parameters)) {
|
||||
$providers[] = $provider;
|
||||
}
|
||||
}
|
||||
|
||||
return $providers;
|
||||
}
|
||||
|
||||
private function countItems($providers, $types, $parameters): int
|
||||
{
|
||||
list($countQuery, $parameters) = $this->buildCountQuery($providers, $types, $parameters);
|
||||
$rsmCount = new ResultSetMappingBuilder($this->em);
|
||||
$rsmCount->addScalarResult('count', 'count');
|
||||
$countNq = $this->em->createNativeQuery($countQuery, $rsmCount);
|
||||
$countNq->setParameters($parameters);
|
||||
|
||||
return (int) $countNq->getSingleScalarResult();
|
||||
}
|
||||
|
||||
private function buildCountQuery(array $queries, $types, $parameters)
|
||||
{
|
||||
$query = "SELECT SUM(c) AS count FROM ({union_unordered}) AS sq";
|
||||
$query = 'SELECT SUM(c) AS count FROM ({union_unordered}) AS sq';
|
||||
$unions = [];
|
||||
$parameters = [];
|
||||
|
||||
foreach ($queries as $q) {
|
||||
$unions[] = $q->buildQuery(true);
|
||||
$parameters = \array_merge($parameters, $q->buildParameters(true));
|
||||
$parameters = array_merge($parameters, $q->buildParameters(true));
|
||||
}
|
||||
|
||||
$unionUnordered = \implode(" UNION ", $unions);
|
||||
$unionUnordered = implode(' UNION ', $unions);
|
||||
|
||||
return [
|
||||
\strtr($query, [ '{union_unordered}' => $unionUnordered ]),
|
||||
$parameters
|
||||
strtr($query, ['{union_unordered}' => $unionUnordered]),
|
||||
$parameters,
|
||||
];
|
||||
}
|
||||
|
||||
private function buildUnionQuery(array $queries, $types, $parameters)
|
||||
{
|
||||
$query = "{unions} ORDER BY pertinence DESC";
|
||||
$unions = [];
|
||||
$parameters = [];
|
||||
|
||||
foreach ($queries as $q) {
|
||||
$unions[] = $q->buildQuery();
|
||||
$parameters = \array_merge($parameters, $q->buildParameters());
|
||||
}
|
||||
|
||||
$union = \implode(" UNION ", $unions);
|
||||
|
||||
return [
|
||||
\strtr($query, [ '{unions}' => $union]),
|
||||
$parameters
|
||||
];
|
||||
}
|
||||
|
||||
private function fetchRawResult($queries, $types, $parameters, $paginator): array
|
||||
{
|
||||
list($union, $parameters) = $this->buildUnionQuery($queries, $types, $parameters);
|
||||
$rsm = new ResultSetMappingBuilder($this->em);
|
||||
$rsm->addScalarResult('key', 'key', Types::STRING)
|
||||
->addScalarResult('metadata', 'metadata', Types::JSON)
|
||||
->addScalarResult('pertinence', 'pertinence', Types::FLOAT)
|
||||
;
|
||||
|
||||
$nq = $this->em->createNativeQuery($union, $rsm);
|
||||
$nq->setParameters($parameters);
|
||||
|
||||
return $nq->getResult();
|
||||
}
|
||||
|
||||
private function prepareProviders(array $rawResults)
|
||||
{
|
||||
$metadatas = [];
|
||||
$providers = [];
|
||||
|
||||
foreach ($rawResults as $r) {
|
||||
foreach ($this->providers as $k => $p) {
|
||||
if ($p->supportsResult($r['key'], $r['metadata'])) {
|
||||
$metadatas[$k][] = $r['metadata'];
|
||||
$providers[$k] = $p;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($metadatas as $k => $m) {
|
||||
$providers[$k]->prepare($m);
|
||||
}
|
||||
}
|
||||
|
||||
private function buildResults(array $rawResults): array
|
||||
{
|
||||
$items = [];
|
||||
@@ -170,6 +91,7 @@ class SearchApi
|
||||
->setResult(
|
||||
$p->getResult($r['key'], $r['metadata'], $r['pertinence'])
|
||||
);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -177,4 +99,90 @@ class SearchApi
|
||||
|
||||
return $items;
|
||||
}
|
||||
|
||||
private function buildUnionQuery(array $queries, $types, $parameters)
|
||||
{
|
||||
$query = '{unions} ORDER BY pertinence DESC';
|
||||
$unions = [];
|
||||
$parameters = [];
|
||||
|
||||
foreach ($queries as $q) {
|
||||
$unions[] = $q->buildQuery();
|
||||
$parameters = array_merge($parameters, $q->buildParameters());
|
||||
}
|
||||
|
||||
$union = implode(' UNION ', $unions);
|
||||
|
||||
return [
|
||||
strtr($query, ['{unions}' => $union]),
|
||||
$parameters,
|
||||
];
|
||||
}
|
||||
|
||||
private function countItems($providers, $types, $parameters): int
|
||||
{
|
||||
[$countQuery, $parameters] = $this->buildCountQuery($providers, $types, $parameters);
|
||||
$rsmCount = new ResultSetMappingBuilder($this->em);
|
||||
$rsmCount->addScalarResult('count', 'count');
|
||||
$countNq = $this->em->createNativeQuery($countQuery, $rsmCount);
|
||||
$countNq->setParameters($parameters);
|
||||
|
||||
return (int) $countNq->getSingleScalarResult();
|
||||
}
|
||||
|
||||
private function fetchRawResult($queries, $types, $parameters, $paginator): array
|
||||
{
|
||||
[$union, $parameters] = $this->buildUnionQuery($queries, $types, $parameters);
|
||||
$rsm = new ResultSetMappingBuilder($this->em);
|
||||
$rsm->addScalarResult('key', 'key', Types::STRING)
|
||||
->addScalarResult('metadata', 'metadata', Types::JSON)
|
||||
->addScalarResult('pertinence', 'pertinence', Types::FLOAT);
|
||||
|
||||
$nq = $this->em->createNativeQuery($union, $rsm);
|
||||
$nq->setParameters($parameters);
|
||||
|
||||
return $nq->getResult();
|
||||
}
|
||||
|
||||
private function findProviders(string $pattern, array $types, array $parameters): array
|
||||
{
|
||||
$providers = [];
|
||||
|
||||
foreach ($this->providers as $provider) {
|
||||
if ($provider->supportsTypes($pattern, $types, $parameters)) {
|
||||
$providers[] = $provider;
|
||||
}
|
||||
}
|
||||
|
||||
return $providers;
|
||||
}
|
||||
|
||||
private function findQueries($pattern, array $types, array $parameters): array
|
||||
{
|
||||
return array_map(
|
||||
fn ($p) => $p->provideQuery($pattern, $parameters),
|
||||
$this->findProviders($pattern, $types, $parameters),
|
||||
);
|
||||
}
|
||||
|
||||
private function prepareProviders(array $rawResults)
|
||||
{
|
||||
$metadatas = [];
|
||||
$providers = [];
|
||||
|
||||
foreach ($rawResults as $r) {
|
||||
foreach ($this->providers as $k => $p) {
|
||||
if ($p->supportsResult($r['key'], $r['metadata'])) {
|
||||
$metadatas[$k][] = $r['metadata'];
|
||||
$providers[$k] = $p;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($metadatas as $k => $m) {
|
||||
$providers[$k]->prepare($m);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -1,18 +1,23 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Chill is a software for social workers
|
||||
*
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Chill\MainBundle\Search;
|
||||
|
||||
use Chill\MainBundle\Search\SearchApiQuery;
|
||||
|
||||
interface SearchApiInterface
|
||||
{
|
||||
public function provideQuery(string $pattern, array $parameters): SearchApiQuery;
|
||||
|
||||
public function supportsTypes(string $pattern, array $types, array $parameters): bool;
|
||||
public function getResult(string $key, array $metadata, float $pertinence);
|
||||
|
||||
public function prepare(array $metadatas): void;
|
||||
|
||||
public function provideQuery(string $pattern, array $parameters): SearchApiQuery;
|
||||
|
||||
public function supportsResult(string $key, array $metadatas): bool;
|
||||
|
||||
public function getResult(string $key, array $metadata, float $pertinence);
|
||||
public function supportsTypes(string $pattern, array $types, array $parameters): bool;
|
||||
}
|
||||
|
@@ -1,18 +1,29 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Chill is a software for social workers
|
||||
*
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Chill\MainBundle\Search;
|
||||
|
||||
use RuntimeException;
|
||||
use Throwable;
|
||||
use function implode;
|
||||
|
||||
class SearchApiNoQueryException extends \RuntimeException
|
||||
class SearchApiNoQueryException extends RuntimeException
|
||||
{
|
||||
private string $pattern;
|
||||
private array $types;
|
||||
private array $parameters;
|
||||
|
||||
public function __construct(string $pattern = "", array $types = [], array $parameters = [], $code = 0, Throwable $previous = null)
|
||||
private string $pattern;
|
||||
|
||||
private array $types;
|
||||
|
||||
public function __construct(string $pattern = '', array $types = [], array $parameters = [], $code = 0, ?Throwable $previous = null)
|
||||
{
|
||||
$typesStr = \implode(", ", $types);
|
||||
$typesStr = implode(', ', $types);
|
||||
$message = "No query for this search: pattern : {$pattern}, types: {$typesStr}";
|
||||
$this->pattern = $pattern;
|
||||
$this->types = $types;
|
||||
|
@@ -1,20 +1,42 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Chill is a software for social workers
|
||||
*
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Chill\MainBundle\Search;
|
||||
|
||||
use function array_push;
|
||||
use function implode;
|
||||
use function strtr;
|
||||
|
||||
class SearchApiQuery
|
||||
{
|
||||
private array $select = [];
|
||||
private array $selectParams = [];
|
||||
private ?string $selectKey = null;
|
||||
private array $selectKeyParams = [];
|
||||
private ?string $jsonbMetadata = null;
|
||||
private array $jsonbMetadataParams = [];
|
||||
private ?string $pertinence = null;
|
||||
private array $pertinenceParams = [];
|
||||
private ?string $fromClause = null;
|
||||
|
||||
private array $fromClauseParams = [];
|
||||
|
||||
private ?string $jsonbMetadata = null;
|
||||
|
||||
private array $jsonbMetadataParams = [];
|
||||
|
||||
private ?string $pertinence = null;
|
||||
|
||||
private array $pertinenceParams = [];
|
||||
|
||||
private array $select = [];
|
||||
|
||||
private ?string $selectKey = null;
|
||||
|
||||
private array $selectKeyParams = [];
|
||||
|
||||
private array $selectParams = [];
|
||||
|
||||
private array $whereClauses = [];
|
||||
|
||||
private array $whereClausesParams = [];
|
||||
|
||||
public function addSelectClause(string $select, array $params = []): self
|
||||
@@ -25,6 +47,81 @@ class SearchApiQuery
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a where clause.
|
||||
*
|
||||
* This will add to previous where clauses with and `AND` join
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function andWhereClause(string $whereClause, array $params = []): self
|
||||
{
|
||||
$this->whereClauses[] = $whereClause;
|
||||
array_push($this->whereClausesParams, ...$params);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function buildParameters(bool $countOnly = false): array
|
||||
{
|
||||
if (!$countOnly) {
|
||||
return [
|
||||
...$this->buildSelectParams($countOnly),
|
||||
...$this->fromClauseParams,
|
||||
...$this->whereClausesParams,
|
||||
];
|
||||
}
|
||||
|
||||
return [
|
||||
...$this->fromClauseParams,
|
||||
...$this->whereClausesParams,
|
||||
];
|
||||
}
|
||||
|
||||
public function buildQuery(bool $countOnly = false): string
|
||||
{
|
||||
$isMultiple = count($this->whereClauses);
|
||||
$where =
|
||||
($isMultiple ? '(' : '') .
|
||||
implode(
|
||||
($isMultiple ? ')' : '') . ' AND ' . ($isMultiple ? '(' : ''),
|
||||
$this->whereClauses
|
||||
) .
|
||||
($isMultiple ? ')' : '');
|
||||
|
||||
$select = $this->buildSelectClause($countOnly);
|
||||
|
||||
return strtr('SELECT
|
||||
{select}
|
||||
FROM {from}
|
||||
WHERE {where}
|
||||
', [
|
||||
'{select}' => $select,
|
||||
'{from}' => $this->fromClause,
|
||||
'{where}' => $where,
|
||||
]);
|
||||
}
|
||||
|
||||
public function getFromClause(): string
|
||||
{
|
||||
return $this->fromClause;
|
||||
}
|
||||
|
||||
public function getFromParams(): array
|
||||
{
|
||||
return $this->fromClauseParams;
|
||||
}
|
||||
|
||||
public function getSelectClauses(): array
|
||||
{
|
||||
return $this->select;
|
||||
}
|
||||
|
||||
public function getSelectParams(): array
|
||||
{
|
||||
return $this->selectParams;
|
||||
}
|
||||
|
||||
public function resetSelectClause(): self
|
||||
{
|
||||
$this->select = [];
|
||||
@@ -39,20 +136,10 @@ class SearchApiQuery
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getSelectClauses(): array
|
||||
public function setFromClause(string $fromClause, array $params = []): self
|
||||
{
|
||||
return $this->select;
|
||||
}
|
||||
|
||||
public function getSelectParams(): array
|
||||
{
|
||||
return $this->selectParams;
|
||||
}
|
||||
|
||||
public function setSelectKey(string $selectKey, array $params = []): self
|
||||
{
|
||||
$this->selectKey = $selectKey;
|
||||
$this->selectKeyParams = $params;
|
||||
$this->fromClause = $fromClause;
|
||||
$this->fromClauseParams = $params;
|
||||
|
||||
return $this;
|
||||
}
|
||||
@@ -65,6 +152,14 @@ class SearchApiQuery
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setSelectKey(string $selectKey, array $params = []): self
|
||||
{
|
||||
$this->selectKey = $selectKey;
|
||||
$this->selectKeyParams = $params;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setSelectPertinence(string $pertinence, array $params = []): self
|
||||
{
|
||||
$this->pertinence = $pertinence;
|
||||
@@ -73,27 +168,8 @@ class SearchApiQuery
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setFromClause(string $fromClause, array $params = []): self
|
||||
{
|
||||
$this->fromClause = $fromClause;
|
||||
$this->fromClauseParams = $params;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getFromClause(): string
|
||||
{
|
||||
return $this->fromClause;
|
||||
}
|
||||
|
||||
public function getFromParams(): array
|
||||
{
|
||||
return $this->fromClauseParams;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the where clause and replace all existing ones.
|
||||
*
|
||||
*/
|
||||
public function setWhereClauses(string $whereClause, array $params = []): self
|
||||
{
|
||||
@@ -103,21 +179,27 @@ class SearchApiQuery
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a where clause.
|
||||
*
|
||||
* This will add to previous where clauses with and `AND` join
|
||||
*
|
||||
* @param string $whereClause
|
||||
* @param array $params
|
||||
* @return $this
|
||||
*/
|
||||
public function andWhereClause(string $whereClause, array $params = []): self
|
||||
private function buildSelectClause(bool $countOnly = false): string
|
||||
{
|
||||
$this->whereClauses[] = $whereClause;
|
||||
\array_push($this->whereClausesParams, ...$params);
|
||||
if ($countOnly) {
|
||||
return 'count(*) AS c';
|
||||
}
|
||||
|
||||
return $this;
|
||||
$selects = $this->getSelectClauses();
|
||||
|
||||
if (null !== $this->selectKey) {
|
||||
$selects[] = strtr("'{key}' AS key", ['{key}' => $this->selectKey]);
|
||||
}
|
||||
|
||||
if (null !== $this->jsonbMetadata) {
|
||||
$selects[] = strtr('{metadata} AS metadata', ['{metadata}' => $this->jsonbMetadata]);
|
||||
}
|
||||
|
||||
if (null !== $this->pertinence) {
|
||||
$selects[] = strtr('{pertinence} AS pertinence', ['{pertinence}' => $this->pertinence]);
|
||||
}
|
||||
|
||||
return implode(', ', $selects);
|
||||
}
|
||||
|
||||
private function buildSelectParams(bool $count = false): array
|
||||
@@ -131,76 +213,15 @@ class SearchApiQuery
|
||||
if (null !== $this->selectKey) {
|
||||
$args = [...$args, ...$this->selectKeyParams];
|
||||
}
|
||||
|
||||
if (null !== $this->jsonbMetadata) {
|
||||
$args = [...$args, ...$this->jsonbMetadataParams];
|
||||
}
|
||||
|
||||
if (null !== $this->pertinence) {
|
||||
$args = [...$args, ...$this->pertinenceParams];
|
||||
}
|
||||
|
||||
return $args;
|
||||
}
|
||||
|
||||
private function buildSelectClause(bool $countOnly = false): string
|
||||
{
|
||||
if ($countOnly) {
|
||||
return 'count(*) AS c';
|
||||
}
|
||||
|
||||
$selects = $this->getSelectClauses();
|
||||
|
||||
if (null !== $this->selectKey) {
|
||||
$selects[] = \strtr("'{key}' AS key", [ '{key}' => $this->selectKey ]);
|
||||
}
|
||||
if (null !== $this->jsonbMetadata) {
|
||||
$selects[] = \strtr('{metadata} AS metadata', [ '{metadata}' => $this->jsonbMetadata]);
|
||||
}
|
||||
if (null !== $this->pertinence) {
|
||||
$selects[] = \strtr('{pertinence} AS pertinence', [ '{pertinence}' => $this->pertinence]);
|
||||
}
|
||||
|
||||
return \implode(', ', $selects);
|
||||
}
|
||||
|
||||
public function buildQuery(bool $countOnly = false): string
|
||||
{
|
||||
$isMultiple = count($this->whereClauses);
|
||||
$where =
|
||||
($isMultiple ? '(' : '').
|
||||
\implode(
|
||||
($isMultiple ? ')' : '').' AND '.($isMultiple ? '(' : '')
|
||||
, $this->whereClauses).
|
||||
($isMultiple ? ')' : '')
|
||||
;
|
||||
|
||||
$select = $this->buildSelectClause($countOnly);
|
||||
|
||||
|
||||
return \strtr("SELECT
|
||||
{select}
|
||||
FROM {from}
|
||||
WHERE {where}
|
||||
", [
|
||||
'{select}' => $select,
|
||||
'{from}' => $this->fromClause,
|
||||
'{where}' => $where,
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
public function buildParameters(bool $countOnly = false): array
|
||||
{
|
||||
if (!$countOnly) {
|
||||
return [
|
||||
...$this->buildSelectParams($countOnly),
|
||||
...$this->fromClauseParams,
|
||||
...$this->whereClausesParams,
|
||||
];
|
||||
} else {
|
||||
return [
|
||||
...$this->fromClauseParams,
|
||||
...$this->whereClausesParams,
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -1,5 +1,12 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Chill is a software for social workers
|
||||
*
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Chill\MainBundle\Search;
|
||||
@@ -10,20 +17,21 @@ class SearchApiResult
|
||||
{
|
||||
private float $pertinence;
|
||||
|
||||
private $result;
|
||||
|
||||
private float $relevance;
|
||||
|
||||
private $result;
|
||||
|
||||
public function __construct(float $relevance)
|
||||
{
|
||||
$this->relevance = $relevance;
|
||||
}
|
||||
|
||||
public function setResult($result): self
|
||||
/**
|
||||
* @Serializer\Groups({"read"})
|
||||
*/
|
||||
public function getRelevance(): float
|
||||
{
|
||||
$this->result = $result;
|
||||
|
||||
return $this;
|
||||
return $this->relevance;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -34,11 +42,10 @@ class SearchApiResult
|
||||
return $this->result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @Serializer\Groups({"read"})
|
||||
*/
|
||||
public function getRelevance(): float
|
||||
public function setResult($result): self
|
||||
{
|
||||
return $this->relevance;
|
||||
$this->result = $result;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
|
@@ -1,22 +1,10 @@
|
||||
<?php
|
||||
|
||||
|
||||
/*
|
||||
/**
|
||||
* Chill is a software for social workers
|
||||
*
|
||||
* Copyright (C) 2014, Champs Libres Cooperative SCRLFS, <http://www.champs-libres.coop>
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Chill\MainBundle\Search;
|
||||
@@ -25,76 +13,75 @@ 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é <julien.fastre@champs-libres.coop>
|
||||
*
|
||||
*/
|
||||
interface SearchInterface
|
||||
{
|
||||
const SEARCH_PREVIEW_OPTION = '_search_preview';
|
||||
/**
|
||||
* Supplementary parameters to the query string.
|
||||
*/
|
||||
public const REQUEST_QUERY_KEY_ADD_PARAMETERS = 'add_q';
|
||||
|
||||
/**
|
||||
* Request parameters contained inside the `add_q` parameters
|
||||
* Request parameters contained inside the `add_q` parameters.
|
||||
*/
|
||||
const REQUEST_QUERY_PARAMETERS = '_search_parameters';
|
||||
public const REQUEST_QUERY_PARAMETERS = '_search_parameters';
|
||||
|
||||
public const SEARCH_PREVIEW_OPTION = '_search_preview';
|
||||
|
||||
/**
|
||||
* Supplementary parameters to the query string
|
||||
* the order in which the results will appears in the global view.
|
||||
*
|
||||
* (this may be eventually defined in config.yml)
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
const REQUEST_QUERY_KEY_ADD_PARAMETERS = 'add_q';
|
||||
public function getOrder();
|
||||
|
||||
/**
|
||||
* 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 %}
|
||||
*
|
||||
* **available options** :
|
||||
* - SEARCH_PREVIEW_OPTION (boolean) : if renderResult should return a "preview" of
|
||||
* the results. In this case, a subset of results should be returned, and,
|
||||
* if the query return more results, a button "see all results" should be
|
||||
* displayed at the end of the list.
|
||||
*
|
||||
* **Interaction between `start` and `limit` and pagination : you should
|
||||
* take only the given parameters into account; the results from pagination
|
||||
* should be ignored. (Most of the time, it should be the same).
|
||||
*
|
||||
* @param array $terms 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
|
||||
* @param string $format The format for result
|
||||
* @return string, an HTML string
|
||||
*/
|
||||
public function renderResult(array $terms, $start=0, $limit=50, array $options = array(), $format = 'html');
|
||||
/**
|
||||
* 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 bool
|
||||
*/
|
||||
public function isActiveByDefault();
|
||||
|
||||
/**
|
||||
* 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 bool
|
||||
*/
|
||||
public function isActiveByDefault();
|
||||
|
||||
/**
|
||||
* 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();
|
||||
|
||||
/**
|
||||
* indicate if the implementation supports the given domain
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function supports($domain, $format);
|
||||
/**
|
||||
* 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 %}
|
||||
*
|
||||
* **available options** :
|
||||
* - SEARCH_PREVIEW_OPTION (boolean) : if renderResult should return a "preview" of
|
||||
* the results. In this case, a subset of results should be returned, and,
|
||||
* if the query return more results, a button "see all results" should be
|
||||
* displayed at the end of the list.
|
||||
*
|
||||
* **Interaction between `start` and `limit` and pagination : you should
|
||||
* take only the given parameters into account; the results from pagination
|
||||
* should be ignored. (Most of the time, it should be the same).
|
||||
*
|
||||
* @param array $terms the string to search
|
||||
* @param int $start the first result (for pagination)
|
||||
* @param int $limit the number of result (for pagination)
|
||||
* @param string $format The format for result
|
||||
*
|
||||
* @return string, an HTML string
|
||||
*/
|
||||
public function renderResult(array $terms, $start = 0, $limit = 50, array $options = [], $format = 'html');
|
||||
|
||||
/**
|
||||
* indicate if the implementation supports the given domain.
|
||||
*
|
||||
* @param mixed $domain
|
||||
* @param mixed $format
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function supports($domain, $format);
|
||||
}
|
||||
|
@@ -1,15 +1,21 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Chill is a software for social workers
|
||||
*
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Chill\MainBundle\Search;
|
||||
|
||||
use Chill\MainBundle\Search\SearchInterface;
|
||||
use Chill\MainBundle\Search\HasAdvancedSearchFormInterface;
|
||||
use function array_key_exists;
|
||||
|
||||
/**
|
||||
* a service which gather all search services defined into the bundles
|
||||
* installed into the app.
|
||||
* the service is callable from the container with
|
||||
* $container->get('chill_main.search_provider')
|
||||
* $container->get('chill_main.search_provider').
|
||||
*
|
||||
* the syntax for search string is :
|
||||
* - domain, which begin with `@`. Example: `@person`. Restrict the search to some
|
||||
@@ -19,22 +25,53 @@ use Chill\MainBundle\Search\HasAdvancedSearchFormInterface;
|
||||
* - terms, which are the terms of the search. There are terms with argument (example :
|
||||
* `birthdate:2016-04-01` and the default term, which is the search without argument
|
||||
* and without domain.
|
||||
*
|
||||
*/
|
||||
class SearchProvider
|
||||
{
|
||||
|
||||
/**
|
||||
*
|
||||
* @var SearchInterface[]
|
||||
*/
|
||||
private $searchServices = array();
|
||||
|
||||
/**
|
||||
*
|
||||
* @var HasAdvancedSearchForm[]
|
||||
*/
|
||||
private $hasAdvancedFormSearchServices = array();
|
||||
private $hasAdvancedFormSearchServices = [];
|
||||
|
||||
/**
|
||||
* store string which must be extracted to find default arguments.
|
||||
*
|
||||
* @var string[]
|
||||
*/
|
||||
private $mustBeExtracted = [];
|
||||
|
||||
/**
|
||||
* @var SearchInterface[]
|
||||
*/
|
||||
private $searchServices = [];
|
||||
|
||||
public function addSearchService(SearchInterface $service, $name)
|
||||
{
|
||||
$this->searchServices[$name] = $service;
|
||||
|
||||
if ($service instanceof HasAdvancedSearchFormInterface) {
|
||||
$this->hasAdvancedFormSearchServices[$name] = $service;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* return search services with a specific name, defined in service
|
||||
* definition.
|
||||
*
|
||||
* @param mixed $name
|
||||
*
|
||||
* @throws UnknowSearchNameException if not exists
|
||||
*
|
||||
* @return SearchInterface
|
||||
*/
|
||||
public function getByName($name)
|
||||
{
|
||||
if (isset($this->searchServices[$name])) {
|
||||
return $this->searchServices[$name];
|
||||
}
|
||||
|
||||
throw new UnknowSearchNameException($name);
|
||||
}
|
||||
|
||||
/*
|
||||
* return search services in an array, ordered by
|
||||
@@ -50,39 +87,135 @@ class SearchProvider
|
||||
public function getByOrder()
|
||||
{
|
||||
//sort the array
|
||||
uasort($this->searchServices, function(SearchInterface $a, SearchInterface $b) {
|
||||
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 searchservice with an advanced form, defined in service
|
||||
* definition.
|
||||
*
|
||||
* @param string $name
|
||||
*
|
||||
* @throws UnknowSearchNameException
|
||||
*
|
||||
* @return HasAdvancedSearchForm
|
||||
*/
|
||||
public function getHasAdvancedFormByName($name)
|
||||
{
|
||||
if (array_key_exists($name, $this->hasAdvancedFormSearchServices)) {
|
||||
return $this->hasAdvancedFormSearchServices[$name];
|
||||
}
|
||||
|
||||
throw new UnknowSearchNameException($name);
|
||||
}
|
||||
|
||||
public function getHasAdvancedFormSearchServices()
|
||||
{
|
||||
//sort the array
|
||||
uasort($this->hasAdvancedFormSearchServices, function(SearchInterface $a, SearchInterface $b) {
|
||||
uasort($this->hasAdvancedFormSearchServices, function (SearchInterface $a, SearchInterface $b) {
|
||||
if ($a->getOrder() == $b->getOrder()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return ($a->getOrder() < $b->getOrder()) ? -1 : 1;
|
||||
});
|
||||
|
||||
return $this->hasAdvancedFormSearchServices;
|
||||
}
|
||||
|
||||
public function getResultByName(
|
||||
$pattern,
|
||||
$name,
|
||||
$start = 0,
|
||||
$limit = 50,
|
||||
array $options = [],
|
||||
$format = 'html'
|
||||
)
|
||||
{
|
||||
$terms = $this->parse($pattern);
|
||||
$search = $this->getByName($name);
|
||||
|
||||
if (null !== $terms['_domain'] && !$search->supports($terms['_domain'], $format)) {
|
||||
throw new ParsingException('The domain is not supported for the search name');
|
||||
}
|
||||
|
||||
return $search->renderResult($terms, $start, $limit, $options, $format);
|
||||
}
|
||||
|
||||
/**
|
||||
* parse the search string to extract domain and terms
|
||||
* search through services which supports domain and give
|
||||
* results as an array of resultsfrom different SearchInterface.
|
||||
*
|
||||
* @param string $pattern
|
||||
* @param number $start
|
||||
* @param number $limit
|
||||
* @param string $format
|
||||
*
|
||||
* @throws UnknowSearchDomainException if the domain is unknow
|
||||
*
|
||||
* @return array of results from different SearchInterface
|
||||
*/
|
||||
public function getSearchResults(
|
||||
$pattern,
|
||||
$start = 0,
|
||||
$limit = 50,
|
||||
array $options = [],
|
||||
$format = 'html'
|
||||
)
|
||||
{
|
||||
$terms = $this->parse($pattern);
|
||||
$results = [];
|
||||
|
||||
//sort searchServices by order
|
||||
$sortedSearchServices = [];
|
||||
|
||||
foreach ($this->searchServices as $service) {
|
||||
$sortedSearchServices[$service->getOrder()] = $service;
|
||||
}
|
||||
|
||||
if (null !== $terms['_domain']) {
|
||||
foreach ($sortedSearchServices as $service) {
|
||||
if ($service->supports($terms['_domain'], $format)) {
|
||||
$results[] = $service->renderResult($terms, $start, $limit, $options);
|
||||
}
|
||||
}
|
||||
|
||||
if (count($results) === 0) {
|
||||
throw new UnknowSearchDomainException($terms['_domain']);
|
||||
}
|
||||
} else { // no domain provided, we use default search
|
||||
foreach ($sortedSearchServices as $service) {
|
||||
if ($service->isActiveByDefault()) {
|
||||
$results[] = $service->renderResult($terms, $start, $limit, $options);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//sort array
|
||||
ksort($results);
|
||||
|
||||
return $results;
|
||||
}
|
||||
|
||||
/**
|
||||
* parse the search string to extract domain and terms.
|
||||
*
|
||||
* @param string $pattern
|
||||
*
|
||||
* @return string[] an array where the keys are _domain, _default (residual terms) or term
|
||||
*/
|
||||
public function parse($pattern)
|
||||
{
|
||||
//reset must be extracted
|
||||
$this->mustBeExtracted = array();
|
||||
$this->mustBeExtracted = [];
|
||||
//filter to lower and remove accentued
|
||||
$filteredPattern = mb_strtolower($this->remove_accents($pattern));
|
||||
|
||||
@@ -94,13 +227,27 @@ class SearchProvider
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract the domain of the subject
|
||||
* extract default (residual) arguments.
|
||||
*
|
||||
* @param string $subject
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function extractDefault($subject)
|
||||
{
|
||||
return trim(str_replace($this->mustBeExtracted, '', $subject));
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract the domain of the subject.
|
||||
*
|
||||
* The domain begins with `@`. Example: `@person`, `@report`, ....
|
||||
*
|
||||
* @param type $subject
|
||||
* @return string
|
||||
*
|
||||
* @throws ParsingException
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function extractDomain(&$subject)
|
||||
{
|
||||
@@ -115,12 +262,12 @@ class SearchProvider
|
||||
$this->mustBeExtracted[] = $terms[0][0];
|
||||
}
|
||||
|
||||
return isset($terms[1][0]) ? $terms[1][0] : NULL;
|
||||
return $terms[1][0] ?? null;
|
||||
}
|
||||
|
||||
private function extractTerms(&$subject)
|
||||
{
|
||||
$terms = array();
|
||||
$terms = [];
|
||||
$matches = [];
|
||||
preg_match_all('/([a-z\-]+):(([^"][\S\-]+)|"[^"]*")/', $subject, $matches);
|
||||
|
||||
@@ -128,8 +275,8 @@ class SearchProvider
|
||||
//remove from search pattern
|
||||
$this->mustBeExtracted[] = $matches[0][$key];
|
||||
//strip parenthesis
|
||||
if (mb_substr($match, 0, 1) === '"' &&
|
||||
mb_substr($match, mb_strlen($match) - 1) === '"') {
|
||||
if (mb_substr($match, 0, 1) === '"'
|
||||
&& mb_substr($match, mb_strlen($match) - 1) === '"') {
|
||||
$match = trim(mb_substr($match, 1, mb_strlen($match) - 2));
|
||||
}
|
||||
$terms[$matches[1][$key]] = $match;
|
||||
@@ -138,128 +285,6 @@ class SearchProvider
|
||||
return $terms;
|
||||
}
|
||||
|
||||
/**
|
||||
* store string which must be extracted to find default arguments
|
||||
*
|
||||
* @var string[]
|
||||
*/
|
||||
private $mustBeExtracted = array();
|
||||
|
||||
/**
|
||||
* extract default (residual) arguments
|
||||
*
|
||||
* @param string $subject
|
||||
* @return string
|
||||
*/
|
||||
private function extractDefault($subject)
|
||||
{
|
||||
return trim(str_replace($this->mustBeExtracted, '', $subject));
|
||||
}
|
||||
|
||||
/**
|
||||
* search through services which supports domain and give
|
||||
* results as an array of resultsfrom different SearchInterface
|
||||
*
|
||||
* @param string $pattern
|
||||
* @param number $start
|
||||
* @param number $limit
|
||||
* @param array $options
|
||||
* @param string $format
|
||||
* @return array of results from different SearchInterface
|
||||
* @throws UnknowSearchDomainException if the domain is unknow
|
||||
*/
|
||||
public function getSearchResults($pattern, $start = 0, $limit = 50,
|
||||
array $options = array(), $format = 'html')
|
||||
{
|
||||
$terms = $this->parse($pattern);
|
||||
$results = array();
|
||||
|
||||
//sort searchServices by order
|
||||
$sortedSearchServices = array();
|
||||
foreach($this->searchServices as $service) {
|
||||
$sortedSearchServices[$service->getOrder()] = $service;
|
||||
}
|
||||
|
||||
if ($terms['_domain'] !== NULL) {
|
||||
foreach ($sortedSearchServices as $service) {
|
||||
if ($service->supports($terms['_domain'], $format)) {
|
||||
$results[] = $service->renderResult($terms, $start, $limit, $options);
|
||||
}
|
||||
}
|
||||
|
||||
if (count($results) === 0) {
|
||||
throw new UnknowSearchDomainException($terms['_domain']);
|
||||
}
|
||||
} else { // no domain provided, we use default search
|
||||
foreach($sortedSearchServices as $service) {
|
||||
if ($service->isActiveByDefault()) {
|
||||
$results[] = $service->renderResult($terms, $start, $limit, $options);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//sort array
|
||||
ksort($results);
|
||||
|
||||
return $results;
|
||||
}
|
||||
|
||||
public function getResultByName($pattern, $name, $start = 0, $limit = 50,
|
||||
array $options = array(), $format = 'html')
|
||||
{
|
||||
$terms = $this->parse($pattern);
|
||||
$search = $this->getByName($name);
|
||||
|
||||
if ($terms['_domain'] !== NULL && !$search->supports($terms['_domain'], $format))
|
||||
{
|
||||
throw new ParsingException("The domain is not supported for the search name");
|
||||
}
|
||||
|
||||
return $search->renderResult($terms, $start, $limit, $options, $format);
|
||||
}
|
||||
|
||||
/**
|
||||
* return search services with a specific name, defined in service
|
||||
* definition.
|
||||
*
|
||||
* @return SearchInterface
|
||||
* @throws UnknowSearchNameException if not exists
|
||||
*/
|
||||
public function getByName($name)
|
||||
{
|
||||
if (isset($this->searchServices[$name])) {
|
||||
return $this->searchServices[$name];
|
||||
} else {
|
||||
throw new UnknowSearchNameException($name);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* return searchservice with an advanced form, defined in service
|
||||
* definition.
|
||||
*
|
||||
* @param string $name
|
||||
* @return HasAdvancedSearchForm
|
||||
* @throws UnknowSearchNameException
|
||||
*/
|
||||
public function getHasAdvancedFormByName($name)
|
||||
{
|
||||
if (\array_key_exists($name, $this->hasAdvancedFormSearchServices)) {
|
||||
return $this->hasAdvancedFormSearchServices[$name];
|
||||
} else {
|
||||
throw new UnknowSearchNameException($name);
|
||||
}
|
||||
}
|
||||
|
||||
public function addSearchService(SearchInterface $service, $name)
|
||||
{
|
||||
$this->searchServices[$name] = $service;
|
||||
|
||||
if ($service instanceof HasAdvancedSearchFormInterface) {
|
||||
$this->hasAdvancedFormSearchServices[$name] = $service;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts all accent characters to ASCII characters.
|
||||
*
|
||||
@@ -268,191 +293,194 @@ class SearchProvider
|
||||
* Imported from wordpress : https://core.trac.wordpress.org/browser/tags/4.1/src/wp-includes/formatting.php?order=name
|
||||
*
|
||||
* @param string $string Text that might have accent characters
|
||||
*
|
||||
* @return string Filtered string with replaced "nice" characters.
|
||||
*
|
||||
* @license GNU GPLv2 : https://core.trac.wordpress.org/browser/tags/4.1/src/license.txt
|
||||
*/
|
||||
private function remove_accents($string)
|
||||
{
|
||||
if (!preg_match('/[\x80-\xff]/', $string))
|
||||
if (!preg_match('/[\x80-\xff]/', $string)) {
|
||||
return $string;
|
||||
}
|
||||
|
||||
//if ($this->seems_utf8($string)) { // remove from wordpress: we use only UTF-8
|
||||
if (true) {
|
||||
$chars = array(
|
||||
// Decompositions for Latin-1 Supplement
|
||||
chr(194) . chr(170) => 'a', chr(194) . chr(186) => 'o',
|
||||
chr(195) . chr(128) => 'A', chr(195) . chr(129) => 'A',
|
||||
chr(195) . chr(130) => 'A', chr(195) . chr(131) => 'A',
|
||||
chr(195) . chr(132) => 'A', chr(195) . chr(133) => 'A',
|
||||
chr(195) . chr(134) => 'AE', chr(195) . chr(135) => 'C',
|
||||
chr(195) . chr(136) => 'E', chr(195) . chr(137) => 'E',
|
||||
chr(195) . chr(138) => 'E', chr(195) . chr(139) => 'E',
|
||||
chr(195) . chr(140) => 'I', chr(195) . chr(141) => 'I',
|
||||
chr(195) . chr(142) => 'I', chr(195) . chr(143) => 'I',
|
||||
chr(195) . chr(144) => 'D', chr(195) . chr(145) => 'N',
|
||||
chr(195) . chr(146) => 'O', chr(195) . chr(147) => 'O',
|
||||
chr(195) . chr(148) => 'O', chr(195) . chr(149) => 'O',
|
||||
chr(195) . chr(150) => 'O', chr(195) . chr(153) => 'U',
|
||||
chr(195) . chr(154) => 'U', chr(195) . chr(155) => 'U',
|
||||
chr(195) . chr(156) => 'U', chr(195) . chr(157) => 'Y',
|
||||
chr(195) . chr(158) => 'TH', chr(195) . chr(159) => 's',
|
||||
chr(195) . chr(160) => 'a', chr(195) . chr(161) => 'a',
|
||||
chr(195) . chr(162) => 'a', chr(195) . chr(163) => 'a',
|
||||
chr(195) . chr(164) => 'a', chr(195) . chr(165) => 'a',
|
||||
chr(195) . chr(166) => 'ae', chr(195) . chr(167) => 'c',
|
||||
chr(195) . chr(168) => 'e', chr(195) . chr(169) => 'e',
|
||||
chr(195) . chr(170) => 'e', chr(195) . chr(171) => 'e',
|
||||
chr(195) . chr(172) => 'i', chr(195) . chr(173) => 'i',
|
||||
chr(195) . chr(174) => 'i', chr(195) . chr(175) => 'i',
|
||||
chr(195) . chr(176) => 'd', chr(195) . chr(177) => 'n',
|
||||
chr(195) . chr(178) => 'o', chr(195) . chr(179) => 'o',
|
||||
chr(195) . chr(180) => 'o', chr(195) . chr(181) => 'o',
|
||||
chr(195) . chr(182) => 'o', chr(195) . chr(184) => 'o',
|
||||
chr(195) . chr(185) => 'u', chr(195) . chr(186) => 'u',
|
||||
chr(195) . chr(187) => 'u', chr(195) . chr(188) => 'u',
|
||||
chr(195) . chr(189) => 'y', chr(195) . chr(190) => 'th',
|
||||
chr(195) . chr(191) => 'y', chr(195) . chr(152) => 'O',
|
||||
// Decompositions for Latin Extended-A
|
||||
chr(196) . chr(128) => 'A', chr(196) . chr(129) => 'a',
|
||||
chr(196) . chr(130) => 'A', chr(196) . chr(131) => 'a',
|
||||
chr(196) . chr(132) => 'A', chr(196) . chr(133) => 'a',
|
||||
chr(196) . chr(134) => 'C', chr(196) . chr(135) => 'c',
|
||||
chr(196) . chr(136) => 'C', chr(196) . chr(137) => 'c',
|
||||
chr(196) . chr(138) => 'C', chr(196) . chr(139) => 'c',
|
||||
chr(196) . chr(140) => 'C', chr(196) . chr(141) => 'c',
|
||||
chr(196) . chr(142) => 'D', chr(196) . chr(143) => 'd',
|
||||
chr(196) . chr(144) => 'D', chr(196) . chr(145) => 'd',
|
||||
chr(196) . chr(146) => 'E', chr(196) . chr(147) => 'e',
|
||||
chr(196) . chr(148) => 'E', chr(196) . chr(149) => 'e',
|
||||
chr(196) . chr(150) => 'E', chr(196) . chr(151) => 'e',
|
||||
chr(196) . chr(152) => 'E', chr(196) . chr(153) => 'e',
|
||||
chr(196) . chr(154) => 'E', chr(196) . chr(155) => 'e',
|
||||
chr(196) . chr(156) => 'G', chr(196) . chr(157) => 'g',
|
||||
chr(196) . chr(158) => 'G', chr(196) . chr(159) => 'g',
|
||||
chr(196) . chr(160) => 'G', chr(196) . chr(161) => 'g',
|
||||
chr(196) . chr(162) => 'G', chr(196) . chr(163) => 'g',
|
||||
chr(196) . chr(164) => 'H', chr(196) . chr(165) => 'h',
|
||||
chr(196) . chr(166) => 'H', chr(196) . chr(167) => 'h',
|
||||
chr(196) . chr(168) => 'I', chr(196) . chr(169) => 'i',
|
||||
chr(196) . chr(170) => 'I', chr(196) . chr(171) => 'i',
|
||||
chr(196) . chr(172) => 'I', chr(196) . chr(173) => 'i',
|
||||
chr(196) . chr(174) => 'I', chr(196) . chr(175) => 'i',
|
||||
chr(196) . chr(176) => 'I', chr(196) . chr(177) => 'i',
|
||||
chr(196) . chr(178) => 'IJ', chr(196) . chr(179) => 'ij',
|
||||
chr(196) . chr(180) => 'J', chr(196) . chr(181) => 'j',
|
||||
chr(196) . chr(182) => 'K', chr(196) . chr(183) => 'k',
|
||||
chr(196) . chr(184) => 'k', chr(196) . chr(185) => 'L',
|
||||
chr(196) . chr(186) => 'l', chr(196) . chr(187) => 'L',
|
||||
chr(196) . chr(188) => 'l', chr(196) . chr(189) => 'L',
|
||||
chr(196) . chr(190) => 'l', chr(196) . chr(191) => 'L',
|
||||
chr(197) . chr(128) => 'l', chr(197) . chr(129) => 'L',
|
||||
chr(197) . chr(130) => 'l', chr(197) . chr(131) => 'N',
|
||||
chr(197) . chr(132) => 'n', chr(197) . chr(133) => 'N',
|
||||
chr(197) . chr(134) => 'n', chr(197) . chr(135) => 'N',
|
||||
chr(197) . chr(136) => 'n', chr(197) . chr(137) => 'N',
|
||||
chr(197) . chr(138) => 'n', chr(197) . chr(139) => 'N',
|
||||
chr(197) . chr(140) => 'O', chr(197) . chr(141) => 'o',
|
||||
chr(197) . chr(142) => 'O', chr(197) . chr(143) => 'o',
|
||||
chr(197) . chr(144) => 'O', chr(197) . chr(145) => 'o',
|
||||
chr(197) . chr(146) => 'OE', chr(197) . chr(147) => 'oe',
|
||||
chr(197) . chr(148) => 'R', chr(197) . chr(149) => 'r',
|
||||
chr(197) . chr(150) => 'R', chr(197) . chr(151) => 'r',
|
||||
chr(197) . chr(152) => 'R', chr(197) . chr(153) => 'r',
|
||||
chr(197) . chr(154) => 'S', chr(197) . chr(155) => 's',
|
||||
chr(197) . chr(156) => 'S', chr(197) . chr(157) => 's',
|
||||
chr(197) . chr(158) => 'S', chr(197) . chr(159) => 's',
|
||||
chr(197) . chr(160) => 'S', chr(197) . chr(161) => 's',
|
||||
chr(197) . chr(162) => 'T', chr(197) . chr(163) => 't',
|
||||
chr(197) . chr(164) => 'T', chr(197) . chr(165) => 't',
|
||||
chr(197) . chr(166) => 'T', chr(197) . chr(167) => 't',
|
||||
chr(197) . chr(168) => 'U', chr(197) . chr(169) => 'u',
|
||||
chr(197) . chr(170) => 'U', chr(197) . chr(171) => 'u',
|
||||
chr(197) . chr(172) => 'U', chr(197) . chr(173) => 'u',
|
||||
chr(197) . chr(174) => 'U', chr(197) . chr(175) => 'u',
|
||||
chr(197) . chr(176) => 'U', chr(197) . chr(177) => 'u',
|
||||
chr(197) . chr(178) => 'U', chr(197) . chr(179) => 'u',
|
||||
chr(197) . chr(180) => 'W', chr(197) . chr(181) => 'w',
|
||||
chr(197) . chr(182) => 'Y', chr(197) . chr(183) => 'y',
|
||||
chr(197) . chr(184) => 'Y', chr(197) . chr(185) => 'Z',
|
||||
chr(197) . chr(186) => 'z', chr(197) . chr(187) => 'Z',
|
||||
chr(197) . chr(188) => 'z', chr(197) . chr(189) => 'Z',
|
||||
chr(197) . chr(190) => 'z', chr(197) . chr(191) => 's',
|
||||
// Decompositions for Latin Extended-B
|
||||
chr(200) . chr(152) => 'S', chr(200) . chr(153) => 's',
|
||||
chr(200) . chr(154) => 'T', chr(200) . chr(155) => 't',
|
||||
// Euro Sign
|
||||
chr(226) . chr(130) . chr(172) => 'E',
|
||||
// GBP (Pound) Sign
|
||||
chr(194) . chr(163) => '',
|
||||
// Vowels with diacritic (Vietnamese)
|
||||
// unmarked
|
||||
chr(198) . chr(160) => 'O', chr(198) . chr(161) => 'o',
|
||||
chr(198) . chr(175) => 'U', chr(198) . chr(176) => 'u',
|
||||
// grave accent
|
||||
chr(225) . chr(186) . chr(166) => 'A', chr(225) . chr(186) . chr(167) => 'a',
|
||||
chr(225) . chr(186) . chr(176) => 'A', chr(225) . chr(186) . chr(177) => 'a',
|
||||
chr(225) . chr(187) . chr(128) => 'E', chr(225) . chr(187) . chr(129) => 'e',
|
||||
chr(225) . chr(187) . chr(146) => 'O', chr(225) . chr(187) . chr(147) => 'o',
|
||||
chr(225) . chr(187) . chr(156) => 'O', chr(225) . chr(187) . chr(157) => 'o',
|
||||
chr(225) . chr(187) . chr(170) => 'U', chr(225) . chr(187) . chr(171) => 'u',
|
||||
chr(225) . chr(187) . chr(178) => 'Y', chr(225) . chr(187) . chr(179) => 'y',
|
||||
// hook
|
||||
chr(225) . chr(186) . chr(162) => 'A', chr(225) . chr(186) . chr(163) => 'a',
|
||||
chr(225) . chr(186) . chr(168) => 'A', chr(225) . chr(186) . chr(169) => 'a',
|
||||
chr(225) . chr(186) . chr(178) => 'A', chr(225) . chr(186) . chr(179) => 'a',
|
||||
chr(225) . chr(186) . chr(186) => 'E', chr(225) . chr(186) . chr(187) => 'e',
|
||||
chr(225) . chr(187) . chr(130) => 'E', chr(225) . chr(187) . chr(131) => 'e',
|
||||
chr(225) . chr(187) . chr(136) => 'I', chr(225) . chr(187) . chr(137) => 'i',
|
||||
chr(225) . chr(187) . chr(142) => 'O', chr(225) . chr(187) . chr(143) => 'o',
|
||||
chr(225) . chr(187) . chr(148) => 'O', chr(225) . chr(187) . chr(149) => 'o',
|
||||
chr(225) . chr(187) . chr(158) => 'O', chr(225) . chr(187) . chr(159) => 'o',
|
||||
chr(225) . chr(187) . chr(166) => 'U', chr(225) . chr(187) . chr(167) => 'u',
|
||||
chr(225) . chr(187) . chr(172) => 'U', chr(225) . chr(187) . chr(173) => 'u',
|
||||
chr(225) . chr(187) . chr(182) => 'Y', chr(225) . chr(187) . chr(183) => 'y',
|
||||
// tilde
|
||||
chr(225) . chr(186) . chr(170) => 'A', chr(225) . chr(186) . chr(171) => 'a',
|
||||
chr(225) . chr(186) . chr(180) => 'A', chr(225) . chr(186) . chr(181) => 'a',
|
||||
chr(225) . chr(186) . chr(188) => 'E', chr(225) . chr(186) . chr(189) => 'e',
|
||||
chr(225) . chr(187) . chr(132) => 'E', chr(225) . chr(187) . chr(133) => 'e',
|
||||
chr(225) . chr(187) . chr(150) => 'O', chr(225) . chr(187) . chr(151) => 'o',
|
||||
chr(225) . chr(187) . chr(160) => 'O', chr(225) . chr(187) . chr(161) => 'o',
|
||||
chr(225) . chr(187) . chr(174) => 'U', chr(225) . chr(187) . chr(175) => 'u',
|
||||
chr(225) . chr(187) . chr(184) => 'Y', chr(225) . chr(187) . chr(185) => 'y',
|
||||
// acute accent
|
||||
chr(225) . chr(186) . chr(164) => 'A', chr(225) . chr(186) . chr(165) => 'a',
|
||||
chr(225) . chr(186) . chr(174) => 'A', chr(225) . chr(186) . chr(175) => 'a',
|
||||
chr(225) . chr(186) . chr(190) => 'E', chr(225) . chr(186) . chr(191) => 'e',
|
||||
chr(225) . chr(187) . chr(144) => 'O', chr(225) . chr(187) . chr(145) => 'o',
|
||||
chr(225) . chr(187) . chr(154) => 'O', chr(225) . chr(187) . chr(155) => 'o',
|
||||
chr(225) . chr(187) . chr(168) => 'U', chr(225) . chr(187) . chr(169) => 'u',
|
||||
// dot below
|
||||
chr(225) . chr(186) . chr(160) => 'A', chr(225) . chr(186) . chr(161) => 'a',
|
||||
chr(225) . chr(186) . chr(172) => 'A', chr(225) . chr(186) . chr(173) => 'a',
|
||||
chr(225) . chr(186) . chr(182) => 'A', chr(225) . chr(186) . chr(183) => 'a',
|
||||
chr(225) . chr(186) . chr(184) => 'E', chr(225) . chr(186) . chr(185) => 'e',
|
||||
chr(225) . chr(187) . chr(134) => 'E', chr(225) . chr(187) . chr(135) => 'e',
|
||||
chr(225) . chr(187) . chr(138) => 'I', chr(225) . chr(187) . chr(139) => 'i',
|
||||
chr(225) . chr(187) . chr(140) => 'O', chr(225) . chr(187) . chr(141) => 'o',
|
||||
chr(225) . chr(187) . chr(152) => 'O', chr(225) . chr(187) . chr(153) => 'o',
|
||||
chr(225) . chr(187) . chr(162) => 'O', chr(225) . chr(187) . chr(163) => 'o',
|
||||
chr(225) . chr(187) . chr(164) => 'U', chr(225) . chr(187) . chr(165) => 'u',
|
||||
chr(225) . chr(187) . chr(176) => 'U', chr(225) . chr(187) . chr(177) => 'u',
|
||||
chr(225) . chr(187) . chr(180) => 'Y', chr(225) . chr(187) . chr(181) => 'y',
|
||||
// Vowels with diacritic (Chinese, Hanyu Pinyin)
|
||||
chr(201) . chr(145) => 'a',
|
||||
// macron
|
||||
chr(199) . chr(149) => 'U', chr(199) . chr(150) => 'u',
|
||||
// acute accent
|
||||
chr(199) . chr(151) => 'U', chr(199) . chr(152) => 'u',
|
||||
// caron
|
||||
chr(199) . chr(141) => 'A', chr(199) . chr(142) => 'a',
|
||||
chr(199) . chr(143) => 'I', chr(199) . chr(144) => 'i',
|
||||
chr(199) . chr(145) => 'O', chr(199) . chr(146) => 'o',
|
||||
chr(199) . chr(147) => 'U', chr(199) . chr(148) => 'u',
|
||||
chr(199) . chr(153) => 'U', chr(199) . chr(154) => 'u',
|
||||
// grave accent
|
||||
chr(199) . chr(155) => 'U', chr(199) . chr(156) => 'u',
|
||||
);
|
||||
$chars = [
|
||||
// Decompositions for Latin-1 Supplement
|
||||
chr(194) . chr(170) => 'a', chr(194) . chr(186) => 'o',
|
||||
chr(195) . chr(128) => 'A', chr(195) . chr(129) => 'A',
|
||||
chr(195) . chr(130) => 'A', chr(195) . chr(131) => 'A',
|
||||
chr(195) . chr(132) => 'A', chr(195) . chr(133) => 'A',
|
||||
chr(195) . chr(134) => 'AE', chr(195) . chr(135) => 'C',
|
||||
chr(195) . chr(136) => 'E', chr(195) . chr(137) => 'E',
|
||||
chr(195) . chr(138) => 'E', chr(195) . chr(139) => 'E',
|
||||
chr(195) . chr(140) => 'I', chr(195) . chr(141) => 'I',
|
||||
chr(195) . chr(142) => 'I', chr(195) . chr(143) => 'I',
|
||||
chr(195) . chr(144) => 'D', chr(195) . chr(145) => 'N',
|
||||
chr(195) . chr(146) => 'O', chr(195) . chr(147) => 'O',
|
||||
chr(195) . chr(148) => 'O', chr(195) . chr(149) => 'O',
|
||||
chr(195) . chr(150) => 'O', chr(195) . chr(153) => 'U',
|
||||
chr(195) . chr(154) => 'U', chr(195) . chr(155) => 'U',
|
||||
chr(195) . chr(156) => 'U', chr(195) . chr(157) => 'Y',
|
||||
chr(195) . chr(158) => 'TH', chr(195) . chr(159) => 's',
|
||||
chr(195) . chr(160) => 'a', chr(195) . chr(161) => 'a',
|
||||
chr(195) . chr(162) => 'a', chr(195) . chr(163) => 'a',
|
||||
chr(195) . chr(164) => 'a', chr(195) . chr(165) => 'a',
|
||||
chr(195) . chr(166) => 'ae', chr(195) . chr(167) => 'c',
|
||||
chr(195) . chr(168) => 'e', chr(195) . chr(169) => 'e',
|
||||
chr(195) . chr(170) => 'e', chr(195) . chr(171) => 'e',
|
||||
chr(195) . chr(172) => 'i', chr(195) . chr(173) => 'i',
|
||||
chr(195) . chr(174) => 'i', chr(195) . chr(175) => 'i',
|
||||
chr(195) . chr(176) => 'd', chr(195) . chr(177) => 'n',
|
||||
chr(195) . chr(178) => 'o', chr(195) . chr(179) => 'o',
|
||||
chr(195) . chr(180) => 'o', chr(195) . chr(181) => 'o',
|
||||
chr(195) . chr(182) => 'o', chr(195) . chr(184) => 'o',
|
||||
chr(195) . chr(185) => 'u', chr(195) . chr(186) => 'u',
|
||||
chr(195) . chr(187) => 'u', chr(195) . chr(188) => 'u',
|
||||
chr(195) . chr(189) => 'y', chr(195) . chr(190) => 'th',
|
||||
chr(195) . chr(191) => 'y', chr(195) . chr(152) => 'O',
|
||||
// Decompositions for Latin Extended-A
|
||||
chr(196) . chr(128) => 'A', chr(196) . chr(129) => 'a',
|
||||
chr(196) . chr(130) => 'A', chr(196) . chr(131) => 'a',
|
||||
chr(196) . chr(132) => 'A', chr(196) . chr(133) => 'a',
|
||||
chr(196) . chr(134) => 'C', chr(196) . chr(135) => 'c',
|
||||
chr(196) . chr(136) => 'C', chr(196) . chr(137) => 'c',
|
||||
chr(196) . chr(138) => 'C', chr(196) . chr(139) => 'c',
|
||||
chr(196) . chr(140) => 'C', chr(196) . chr(141) => 'c',
|
||||
chr(196) . chr(142) => 'D', chr(196) . chr(143) => 'd',
|
||||
chr(196) . chr(144) => 'D', chr(196) . chr(145) => 'd',
|
||||
chr(196) . chr(146) => 'E', chr(196) . chr(147) => 'e',
|
||||
chr(196) . chr(148) => 'E', chr(196) . chr(149) => 'e',
|
||||
chr(196) . chr(150) => 'E', chr(196) . chr(151) => 'e',
|
||||
chr(196) . chr(152) => 'E', chr(196) . chr(153) => 'e',
|
||||
chr(196) . chr(154) => 'E', chr(196) . chr(155) => 'e',
|
||||
chr(196) . chr(156) => 'G', chr(196) . chr(157) => 'g',
|
||||
chr(196) . chr(158) => 'G', chr(196) . chr(159) => 'g',
|
||||
chr(196) . chr(160) => 'G', chr(196) . chr(161) => 'g',
|
||||
chr(196) . chr(162) => 'G', chr(196) . chr(163) => 'g',
|
||||
chr(196) . chr(164) => 'H', chr(196) . chr(165) => 'h',
|
||||
chr(196) . chr(166) => 'H', chr(196) . chr(167) => 'h',
|
||||
chr(196) . chr(168) => 'I', chr(196) . chr(169) => 'i',
|
||||
chr(196) . chr(170) => 'I', chr(196) . chr(171) => 'i',
|
||||
chr(196) . chr(172) => 'I', chr(196) . chr(173) => 'i',
|
||||
chr(196) . chr(174) => 'I', chr(196) . chr(175) => 'i',
|
||||
chr(196) . chr(176) => 'I', chr(196) . chr(177) => 'i',
|
||||
chr(196) . chr(178) => 'IJ', chr(196) . chr(179) => 'ij',
|
||||
chr(196) . chr(180) => 'J', chr(196) . chr(181) => 'j',
|
||||
chr(196) . chr(182) => 'K', chr(196) . chr(183) => 'k',
|
||||
chr(196) . chr(184) => 'k', chr(196) . chr(185) => 'L',
|
||||
chr(196) . chr(186) => 'l', chr(196) . chr(187) => 'L',
|
||||
chr(196) . chr(188) => 'l', chr(196) . chr(189) => 'L',
|
||||
chr(196) . chr(190) => 'l', chr(196) . chr(191) => 'L',
|
||||
chr(197) . chr(128) => 'l', chr(197) . chr(129) => 'L',
|
||||
chr(197) . chr(130) => 'l', chr(197) . chr(131) => 'N',
|
||||
chr(197) . chr(132) => 'n', chr(197) . chr(133) => 'N',
|
||||
chr(197) . chr(134) => 'n', chr(197) . chr(135) => 'N',
|
||||
chr(197) . chr(136) => 'n', chr(197) . chr(137) => 'N',
|
||||
chr(197) . chr(138) => 'n', chr(197) . chr(139) => 'N',
|
||||
chr(197) . chr(140) => 'O', chr(197) . chr(141) => 'o',
|
||||
chr(197) . chr(142) => 'O', chr(197) . chr(143) => 'o',
|
||||
chr(197) . chr(144) => 'O', chr(197) . chr(145) => 'o',
|
||||
chr(197) . chr(146) => 'OE', chr(197) . chr(147) => 'oe',
|
||||
chr(197) . chr(148) => 'R', chr(197) . chr(149) => 'r',
|
||||
chr(197) . chr(150) => 'R', chr(197) . chr(151) => 'r',
|
||||
chr(197) . chr(152) => 'R', chr(197) . chr(153) => 'r',
|
||||
chr(197) . chr(154) => 'S', chr(197) . chr(155) => 's',
|
||||
chr(197) . chr(156) => 'S', chr(197) . chr(157) => 's',
|
||||
chr(197) . chr(158) => 'S', chr(197) . chr(159) => 's',
|
||||
chr(197) . chr(160) => 'S', chr(197) . chr(161) => 's',
|
||||
chr(197) . chr(162) => 'T', chr(197) . chr(163) => 't',
|
||||
chr(197) . chr(164) => 'T', chr(197) . chr(165) => 't',
|
||||
chr(197) . chr(166) => 'T', chr(197) . chr(167) => 't',
|
||||
chr(197) . chr(168) => 'U', chr(197) . chr(169) => 'u',
|
||||
chr(197) . chr(170) => 'U', chr(197) . chr(171) => 'u',
|
||||
chr(197) . chr(172) => 'U', chr(197) . chr(173) => 'u',
|
||||
chr(197) . chr(174) => 'U', chr(197) . chr(175) => 'u',
|
||||
chr(197) . chr(176) => 'U', chr(197) . chr(177) => 'u',
|
||||
chr(197) . chr(178) => 'U', chr(197) . chr(179) => 'u',
|
||||
chr(197) . chr(180) => 'W', chr(197) . chr(181) => 'w',
|
||||
chr(197) . chr(182) => 'Y', chr(197) . chr(183) => 'y',
|
||||
chr(197) . chr(184) => 'Y', chr(197) . chr(185) => 'Z',
|
||||
chr(197) . chr(186) => 'z', chr(197) . chr(187) => 'Z',
|
||||
chr(197) . chr(188) => 'z', chr(197) . chr(189) => 'Z',
|
||||
chr(197) . chr(190) => 'z', chr(197) . chr(191) => 's',
|
||||
// Decompositions for Latin Extended-B
|
||||
chr(200) . chr(152) => 'S', chr(200) . chr(153) => 's',
|
||||
chr(200) . chr(154) => 'T', chr(200) . chr(155) => 't',
|
||||
// Euro Sign
|
||||
chr(226) . chr(130) . chr(172) => 'E',
|
||||
// GBP (Pound) Sign
|
||||
chr(194) . chr(163) => '',
|
||||
// Vowels with diacritic (Vietnamese)
|
||||
// unmarked
|
||||
chr(198) . chr(160) => 'O', chr(198) . chr(161) => 'o',
|
||||
chr(198) . chr(175) => 'U', chr(198) . chr(176) => 'u',
|
||||
// grave accent
|
||||
chr(225) . chr(186) . chr(166) => 'A', chr(225) . chr(186) . chr(167) => 'a',
|
||||
chr(225) . chr(186) . chr(176) => 'A', chr(225) . chr(186) . chr(177) => 'a',
|
||||
chr(225) . chr(187) . chr(128) => 'E', chr(225) . chr(187) . chr(129) => 'e',
|
||||
chr(225) . chr(187) . chr(146) => 'O', chr(225) . chr(187) . chr(147) => 'o',
|
||||
chr(225) . chr(187) . chr(156) => 'O', chr(225) . chr(187) . chr(157) => 'o',
|
||||
chr(225) . chr(187) . chr(170) => 'U', chr(225) . chr(187) . chr(171) => 'u',
|
||||
chr(225) . chr(187) . chr(178) => 'Y', chr(225) . chr(187) . chr(179) => 'y',
|
||||
// hook
|
||||
chr(225) . chr(186) . chr(162) => 'A', chr(225) . chr(186) . chr(163) => 'a',
|
||||
chr(225) . chr(186) . chr(168) => 'A', chr(225) . chr(186) . chr(169) => 'a',
|
||||
chr(225) . chr(186) . chr(178) => 'A', chr(225) . chr(186) . chr(179) => 'a',
|
||||
chr(225) . chr(186) . chr(186) => 'E', chr(225) . chr(186) . chr(187) => 'e',
|
||||
chr(225) . chr(187) . chr(130) => 'E', chr(225) . chr(187) . chr(131) => 'e',
|
||||
chr(225) . chr(187) . chr(136) => 'I', chr(225) . chr(187) . chr(137) => 'i',
|
||||
chr(225) . chr(187) . chr(142) => 'O', chr(225) . chr(187) . chr(143) => 'o',
|
||||
chr(225) . chr(187) . chr(148) => 'O', chr(225) . chr(187) . chr(149) => 'o',
|
||||
chr(225) . chr(187) . chr(158) => 'O', chr(225) . chr(187) . chr(159) => 'o',
|
||||
chr(225) . chr(187) . chr(166) => 'U', chr(225) . chr(187) . chr(167) => 'u',
|
||||
chr(225) . chr(187) . chr(172) => 'U', chr(225) . chr(187) . chr(173) => 'u',
|
||||
chr(225) . chr(187) . chr(182) => 'Y', chr(225) . chr(187) . chr(183) => 'y',
|
||||
// tilde
|
||||
chr(225) . chr(186) . chr(170) => 'A', chr(225) . chr(186) . chr(171) => 'a',
|
||||
chr(225) . chr(186) . chr(180) => 'A', chr(225) . chr(186) . chr(181) => 'a',
|
||||
chr(225) . chr(186) . chr(188) => 'E', chr(225) . chr(186) . chr(189) => 'e',
|
||||
chr(225) . chr(187) . chr(132) => 'E', chr(225) . chr(187) . chr(133) => 'e',
|
||||
chr(225) . chr(187) . chr(150) => 'O', chr(225) . chr(187) . chr(151) => 'o',
|
||||
chr(225) . chr(187) . chr(160) => 'O', chr(225) . chr(187) . chr(161) => 'o',
|
||||
chr(225) . chr(187) . chr(174) => 'U', chr(225) . chr(187) . chr(175) => 'u',
|
||||
chr(225) . chr(187) . chr(184) => 'Y', chr(225) . chr(187) . chr(185) => 'y',
|
||||
// acute accent
|
||||
chr(225) . chr(186) . chr(164) => 'A', chr(225) . chr(186) . chr(165) => 'a',
|
||||
chr(225) . chr(186) . chr(174) => 'A', chr(225) . chr(186) . chr(175) => 'a',
|
||||
chr(225) . chr(186) . chr(190) => 'E', chr(225) . chr(186) . chr(191) => 'e',
|
||||
chr(225) . chr(187) . chr(144) => 'O', chr(225) . chr(187) . chr(145) => 'o',
|
||||
chr(225) . chr(187) . chr(154) => 'O', chr(225) . chr(187) . chr(155) => 'o',
|
||||
chr(225) . chr(187) . chr(168) => 'U', chr(225) . chr(187) . chr(169) => 'u',
|
||||
// dot below
|
||||
chr(225) . chr(186) . chr(160) => 'A', chr(225) . chr(186) . chr(161) => 'a',
|
||||
chr(225) . chr(186) . chr(172) => 'A', chr(225) . chr(186) . chr(173) => 'a',
|
||||
chr(225) . chr(186) . chr(182) => 'A', chr(225) . chr(186) . chr(183) => 'a',
|
||||
chr(225) . chr(186) . chr(184) => 'E', chr(225) . chr(186) . chr(185) => 'e',
|
||||
chr(225) . chr(187) . chr(134) => 'E', chr(225) . chr(187) . chr(135) => 'e',
|
||||
chr(225) . chr(187) . chr(138) => 'I', chr(225) . chr(187) . chr(139) => 'i',
|
||||
chr(225) . chr(187) . chr(140) => 'O', chr(225) . chr(187) . chr(141) => 'o',
|
||||
chr(225) . chr(187) . chr(152) => 'O', chr(225) . chr(187) . chr(153) => 'o',
|
||||
chr(225) . chr(187) . chr(162) => 'O', chr(225) . chr(187) . chr(163) => 'o',
|
||||
chr(225) . chr(187) . chr(164) => 'U', chr(225) . chr(187) . chr(165) => 'u',
|
||||
chr(225) . chr(187) . chr(176) => 'U', chr(225) . chr(187) . chr(177) => 'u',
|
||||
chr(225) . chr(187) . chr(180) => 'Y', chr(225) . chr(187) . chr(181) => 'y',
|
||||
// Vowels with diacritic (Chinese, Hanyu Pinyin)
|
||||
chr(201) . chr(145) => 'a',
|
||||
// macron
|
||||
chr(199) . chr(149) => 'U', chr(199) . chr(150) => 'u',
|
||||
// acute accent
|
||||
chr(199) . chr(151) => 'U', chr(199) . chr(152) => 'u',
|
||||
// caron
|
||||
chr(199) . chr(141) => 'A', chr(199) . chr(142) => 'a',
|
||||
chr(199) . chr(143) => 'I', chr(199) . chr(144) => 'i',
|
||||
chr(199) . chr(145) => 'O', chr(199) . chr(146) => 'o',
|
||||
chr(199) . chr(147) => 'U', chr(199) . chr(148) => 'u',
|
||||
chr(199) . chr(153) => 'U', chr(199) . chr(154) => 'u',
|
||||
// grave accent
|
||||
chr(199) . chr(155) => 'U', chr(199) . chr(156) => 'u',
|
||||
];
|
||||
|
||||
// Used for locale-specific rules
|
||||
/* remove from wordpress
|
||||
@@ -501,5 +529,4 @@ class SearchProvider
|
||||
|
||||
return $string;
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -1,42 +1,30 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
/**
|
||||
* Chill is a software for social workers
|
||||
* Copyright (C) 2014 Champs-Libres Coopérative <info@champs-libres.coop>
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Chill\MainBundle\Search;
|
||||
|
||||
use Exception;
|
||||
|
||||
/**
|
||||
* Throw by search provider when the search name is not found
|
||||
*
|
||||
* @author Julien Fastré <julien.fastre@champs-libres.coop>
|
||||
* Throw by search provider when the search name is not found.
|
||||
*/
|
||||
class UnknowSearchDomainException extends \Exception
|
||||
class UnknowSearchDomainException extends Exception
|
||||
{
|
||||
|
||||
private $domain;
|
||||
|
||||
|
||||
public function __construct($domain)
|
||||
{
|
||||
parent::__construct( "The domain $domain is not found");
|
||||
parent::__construct("The domain {$domain} is not found");
|
||||
$this->domain = $domain;
|
||||
}
|
||||
|
||||
public function getDomain()
|
||||
|
||||
public function getDomain()
|
||||
{
|
||||
return $this->domain;
|
||||
}
|
||||
|
@@ -1,41 +1,29 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
/**
|
||||
* Chill is a software for social workers
|
||||
* Copyright (C) 2014 Champs-Libres Coopérative <info@champs-libres.coop>
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Chill\MainBundle\Search;
|
||||
|
||||
use Exception;
|
||||
|
||||
/**
|
||||
* Throw by search provider when the search name is not found
|
||||
*
|
||||
* @author Julien Fastré <julien.fastre@champs-libres.coop>
|
||||
* Throw by search provider when the search name is not found.
|
||||
*/
|
||||
class UnknowSearchNameException extends \Exception
|
||||
class UnknowSearchNameException extends Exception
|
||||
{
|
||||
|
||||
private $name;
|
||||
|
||||
|
||||
public function __construct($name)
|
||||
{
|
||||
parent::__construct( "No module search supports with the name $name");
|
||||
parent::__construct("No module search supports with the name {$name}");
|
||||
$this->name = $name;
|
||||
}
|
||||
|
||||
|
||||
public function getName()
|
||||
{
|
||||
return $this->name;
|
||||
|
@@ -1,15 +1,25 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Chill is a software for social workers
|
||||
*
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Chill\MainBundle\Search\Utils;
|
||||
|
||||
use \DateTimeImmutable;
|
||||
use DateTimeImmutable;
|
||||
use function preg_match_all;
|
||||
use function strtr;
|
||||
use function trim;
|
||||
|
||||
class ExtractDateFromPattern
|
||||
{
|
||||
private const DATE_PATTERN = [
|
||||
["([12]\d{3}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01]))", 'Y-m-d'], // 1981-05-12
|
||||
["((0[1-9]|[12]\d|3[01])\/(0[1-9]|1[0-2])\/([12]\d{3}))", 'd/m/Y'], // 15/12/1980
|
||||
["((0[1-9]|[12]\d|3[01])-(0[1-9]|1[0-2])-([12]\d{3}))", 'd-m-Y'], // 15/12/1980
|
||||
['([12]\\d{3}-(0[1-9]|1[0-2])-(0[1-9]|[12]\\d|3[01]))', 'Y-m-d'], // 1981-05-12
|
||||
['((0[1-9]|[12]\\d|3[01])\\/(0[1-9]|1[0-2])\\/([12]\\d{3}))', 'd/m/Y'], // 15/12/1980
|
||||
['((0[1-9]|[12]\\d|3[01])-(0[1-9]|1[0-2])-([12]\\d{3}))', 'd-m-Y'], // 15/12/1980
|
||||
];
|
||||
|
||||
public function extractDates(string $subject): SearchExtractionResult
|
||||
@@ -19,14 +29,15 @@ class ExtractDateFromPattern
|
||||
|
||||
foreach (self::DATE_PATTERN as [$pattern, $format]) {
|
||||
$matches = [];
|
||||
\preg_match_all($pattern, $filteredSubject, $matches);
|
||||
preg_match_all($pattern, $filteredSubject, $matches);
|
||||
|
||||
foreach ($matches[0] as $match) {
|
||||
$date = DateTimeImmutable::createFromFormat($format, $match);
|
||||
|
||||
if (false !== $date) {
|
||||
$dates[] = $date;
|
||||
// filter string: remove what is found
|
||||
$filteredSubject = \trim(\strtr($filteredSubject, [$match => ""]));
|
||||
$filteredSubject = trim(strtr($filteredSubject, [$match => '']));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -1,28 +1,47 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Chill is a software for social workers
|
||||
*
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Chill\MainBundle\Search\Utils;
|
||||
|
||||
use LogicException;
|
||||
use function implode;
|
||||
use function preg_match;
|
||||
use function str_split;
|
||||
use function strtr;
|
||||
use function trim;
|
||||
|
||||
class ExtractPhonenumberFromPattern
|
||||
{
|
||||
private const PATTERN = "([\+]{0,1}[0-9\ ]{5,})";
|
||||
private const PATTERN = '([\\+]{0,1}[0-9\\ ]{5,})';
|
||||
|
||||
public function extractPhonenumber(string $subject): SearchExtractionResult
|
||||
{
|
||||
$matches = [];
|
||||
\preg_match(self::PATTERN, $subject,$matches);
|
||||
preg_match(self::PATTERN, $subject, $matches);
|
||||
|
||||
if (0 < count($matches)) {
|
||||
$phonenumber = [];
|
||||
$length = 0;
|
||||
|
||||
foreach (\str_split(\trim($matches[0])) as $key => $char) {
|
||||
foreach (str_split(trim($matches[0])) as $key => $char) {
|
||||
switch ($char) {
|
||||
case '0':
|
||||
$length++;
|
||||
if ($key === 0) { $phonenumber[] = '+32'; }
|
||||
else { $phonenumber[] = $char; }
|
||||
|
||||
if (0 === $key) {
|
||||
$phonenumber[] = '+32';
|
||||
} else {
|
||||
$phonenumber[] = $char;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case '1':
|
||||
case '2':
|
||||
case '3':
|
||||
@@ -34,18 +53,21 @@ class ExtractPhonenumberFromPattern
|
||||
case '9':
|
||||
$length++;
|
||||
$phonenumber[] = $char;
|
||||
|
||||
break;
|
||||
|
||||
case ' ':
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new \LogicException("should not match not alnum character");
|
||||
throw new LogicException('should not match not alnum character');
|
||||
}
|
||||
}
|
||||
|
||||
if ($length > 5) {
|
||||
$filtered = \trim(\strtr($subject, [$matches[0] => '']));
|
||||
if (5 < $length) {
|
||||
$filtered = trim(strtr($subject, [$matches[0] => '']));
|
||||
|
||||
return new SearchExtractionResult($filtered, [\implode('', $phonenumber)] );
|
||||
return new SearchExtractionResult($filtered, [implode('', $phonenumber)]);
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -1,10 +1,18 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Chill is a software for social workers
|
||||
*
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Chill\MainBundle\Search\Utils;
|
||||
|
||||
class SearchExtractionResult
|
||||
{
|
||||
private string $filteredSubject;
|
||||
|
||||
private array $found;
|
||||
|
||||
public function __construct(string $filteredSubject, array $found)
|
||||
@@ -13,6 +21,11 @@ class SearchExtractionResult
|
||||
$this->found = $found;
|
||||
}
|
||||
|
||||
public function getFilteredSubject(): string
|
||||
{
|
||||
return $this->filteredSubject;
|
||||
}
|
||||
|
||||
public function getFound(): array
|
||||
{
|
||||
return $this->found;
|
||||
@@ -22,9 +35,4 @@ class SearchExtractionResult
|
||||
{
|
||||
return [] !== $this->found;
|
||||
}
|
||||
|
||||
public function getFilteredSubject(): string
|
||||
{
|
||||
return $this->filteredSubject;
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user