cs: Fix code style (safe rules only).

This commit is contained in:
Pol Dellaiera
2021-11-23 14:06:38 +01:00
parent 149d7ce991
commit 8f96a1121d
1223 changed files with 65199 additions and 64625 deletions

View File

@@ -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);
}

View File

@@ -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);
}
}

View File

@@ -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);
}

View File

@@ -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;
}
}

View File

@@ -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
{
}
}

View File

@@ -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);
}
}
}

View File

@@ -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;
}

View File

@@ -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;

View File

@@ -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,
];
}
}
}

View File

@@ -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;
}
}

View File

@@ -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);
}

View File

@@ -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;
}
}

View File

@@ -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;
}

View File

@@ -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;

View File

@@ -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 => '']));
}
}
}

View File

@@ -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)]);
}
}

View File

@@ -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;
}
}