Files
materio-d8/web/modules/custom/materio_sapi/src/Controller/Base.php
T

477 lines
16 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
// https://www.drupal.org/docs/8/modules/search-api/developer-documentation/executing-a-search-in-code
namespace Drupal\materio_sapi\Controller;
use Drupal\Core\Controller\ControllerBase;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Drupal\Component\Utility\Tags;
use Drupal\Component\Utility\Unicode;
use Drupal\search_api\Entity\Index;
// https://drupal.stackexchange.com/questions/225008/programatically-use-search-api
/**
* Defines a route controller for materio sapi search (regular and ajax).
*/
class Base extends ControllerBase {
private $limit = 15;
private $offset = 0;
private function sapiQuery(){
// https://www.drupal.org/docs/8/modules/search-api/developer-documentation/executing-a-search-in-code
// https://www.hashbangcode.com/article/drupal-8-date-search-boosting-search-api-and-solr-search
// https://kgaut.net/blog/2018/drupal-8-search-api-effectuer-une-requete-dans-le-code.html
// Otherwise, if you use Solr, you can also set the parse mode to “Direct query”
// and use something like 'tm_name:alex AND tm_surname:john' as the keywords (field names may vary).
// That will also ensure scoring works for the keywords (the other way just adds filters, which dont affect scoring).
// https://www.drupal.org/project/search_api/issues/3049097
$lang = \Drupal::languageManager()->getCurrentLanguage()->getId();
$this->index = Index::load('database');
$this->results = [
'uuids' => [],
'nids' => []
];
// PHRASE QUERY
// (to match exact materials names (like "wood-skin"))
$this->phrase_query = $this->index->query(['offset'=>0,'limit'=>10000]);
// set parse mode and conjunction
$parse_mode = \Drupal::service('plugin.manager.search_api.parse_mode')
->createInstance('phrase');
$parse_mode->setConjunction('AND');
$this->phrase_query->setParseMode($parse_mode);
// Set fulltext search keywords and fields.
if ($this->keys) {
$this->phrase_query->keys(implode(' ', $this->keys));
}
$this->phrase_query->setFulltextFields(['title']);
// Restrict the search to specific languages.
$this->phrase_query->setLanguages([$lang]);
// Do paging.
// $this->and_query->range($this->offset, $this->limit);
// retrieve all results
// $this->and_query->range(0, -1);
// Add sorting.
$this->phrase_query->sort('search_api_relevance', 'DESC');
// Set one or more tags for the query.
// @see hook_search_api_query_TAG_alter()
// @see hook_search_api_results_TAG_alter()
$this->phrase_query->addTag('materio_sapi_search_phrase_query');
$phrase_results = $this->phrase_query->execute();
foreach ($phrase_results as $result) {
$this->results['uuids'][] = $result->getField('uuid')->getValues()[0];
$this->results['nids'][] = $result->getField('nid')->getValues()[0];
}
// AND QUERY
$this->and_query = $this->index->query(['offset'=>0,'limit'=>10000]);
// set parse mode and conjunction
$parse_mode = \Drupal::service('plugin.manager.search_api.parse_mode')
->createInstance('terms');
$parse_mode->setConjunction('AND');
$this->and_query->setParseMode($parse_mode);
// Set fulltext search keywords and fields.
if ($this->keys) {
$this->and_query->keys(implode(' ', $this->keys));
}
// in case of term id provided restrict the keys to taxo fields
if ($this->terms && count($this->terms)) {
$term_conditions = $this->and_query->createConditionGroup('OR');
// $term = (int) $this->term;
foreach ($this->terms as $term) {
$tid = $term->value;
foreach (['tag_tid', 'thesaurus_tid'] as $field) {
$term_conditions->addCondition($field, (int) $tid);
}
}
$this->and_query->addConditionGroup($term_conditions);
// INSTEAD TRY TO BOOST THE TAG AND THESAURUS FIELDS
// foreach ($taxoSolrFieldsName as $fname) {
// // $solarium_query->addParam('bf', "recip(abs(ms(NOW,{$solrField})),3.16e-11,10,0.1)");
// $bfparam = "if(gt(termfreq({$fname},{$this->term}),0),^21,0)";
// $this->or_query->addParam('bf', $bfparam);
// }
// look @ materio_sapi_search_api_solr_query_alter in materio_sapi.module
// $this->or_query->setOption('termid', $this->term);
}
if ($this->filters) {
// FILTERS
$filters_conditions = $this->and_query->createConditionGroup('AND');
foreach ($this->filters as $filter) {
$filter = (int) $filter;
foreach (['thesaurus_tid'] as $field) { // 'tag_tid',
$filters_conditions->addCondition($field, $filter);
}
}
$this->and_query->addConditionGroup($filters_conditions);
if(!$this->keys) {
// if no keys but filters switch query to direct and add wildcard solr keys *:*
$direct_and_parse_mode = \Drupal::service('plugin.manager.search_api.parse_mode')
->createInstance('direct');
$direct_and_parse_mode->setConjunction('AND');
$this->and_query->setParseMode($direct_and_parse_mode);
// $this->and_query->keys('*:*');
}
}
// else{
$fulltextFields = [];
// Recherche uniquement sur les champ thésaurus et tag
$fulltextFields += [
'thesaurus_name_0',
'thesaurus_synonyms_0',
'thesaurus_name_1',
'thesaurus_synonyms_1',
'thesaurus_name_2',
'thesaurus_synonyms_2',
'thesaurus_name_3',
'thesaurus_synonyms_3',
'thesaurus_name_4',
'thesaurus_synonyms_4',
'thesaurus_name',
'thesaurus_synonyms',
'tag_name',
'tag_synonyms'
];
if(count($fulltextFields)){
$this->and_query->setFulltextFields($fulltextFields);
}
// }
// Restrict the search to specific languages.
$this->and_query->setLanguages([$lang]);
// Do paging.
// $this->and_query->range($this->offset, $this->limit);
// retrieve all results
// $this->and_query->range(0, -1);
// Add sorting.
$this->and_query->sort('search_api_relevance', 'DESC');
// Set one or more tags for the query.
// @see hook_search_api_query_TAG_alter()
// @see hook_search_api_results_TAG_alter()
$this->and_query->addTag('materio_sapi_search_and_query');
$and_results = $this->and_query->execute();
foreach ($and_results as $result) {
// !! have to remove duplicates from phrase query
$nid = $result->getField('nid')->getValues()[0];
if ( !in_array($nid, $this->results['nids']) ) {
$this->results['uuids'][] = $result->getField('uuid')->getValues()[0];
$this->results['nids'][] = $result->getField('nid')->getValues()[0];
}
}
$this->exactematch_count = count($this->results['nids']);
//
// OR QUERY
//
$this->or_query = $this->index->query(['offset'=>0,'limit'=>10000]);
// Change the parse mode for the search.
// Les différentes possibilités sont
// - « direct » => Requête directe
// - « terms » => Multiple words
// - « phrase » => Single phrase
// - " edismax " => ???
$or_parse_mode = \Drupal::service('plugin.manager.search_api.parse_mode')
->createInstance('direct');
$or_parse_mode->setConjunction('OR');
$this->or_query->setParseMode($or_parse_mode);
// Set fulltext search keywords and fields.
if ($this->keys) {
$this->or_query->keys(implode(' ', $this->keys));
}
// // exclude results from previous and_query
// !! trigering solr "too many boolean clauses" error
// $exclude_and_results_conditions = $this->or_query->createConditionGroup('AND');
// foreach ($this->results['nids'] as $nid) {
// $exclude_and_results_conditions->addCondition('nid', $nid, '<>');
// }
// $this->or_query->addConditionGroup($exclude_and_results_conditions);
if (preg_match_all('/[WTRPCMFGSO]\d{4}/i', implode(' ', $this->keys), $matches)) {
// in case we search for material references like W0117
$ref_conditions = $this->or_query->createConditionGroup('OR');
foreach ($matches[0] as $key => $value) {
$ref_conditions->addCondition('field_reference', $value);
}
$this->or_query->addConditionGroup($ref_conditions);
}
if ($this->filters) {
// FILTERS
$or_filters_conditions = $this->or_query->createConditionGroup('OR');
foreach ($this->filters as $filter) {
$filter = (int) $filter;
foreach (['thesaurus_tid'] as $field) { // 'tag_tid',
$or_filters_conditions->addCondition($field, $filter);
}
}
$this->or_query->addConditionGroup($or_filters_conditions);
if(!$this->keys) {
// if no keys but filters switch query to direct and add wildcard solr keys *:*
$direct_or_parse_mode = \Drupal::service('plugin.manager.search_api.parse_mode')
->createInstance('direct');
$direct_or_parse_mode->setConjunction('OR');
$this->or_query->setParseMode($direct_or_parse_mode);
// $this->or_query->keys('*:*');
}
}
// Restrict the search to specific languages.
$this->or_query->setLanguages([$lang]);
// Do paging.
// $this->or_query->range($this->offset, $this->limit);
// retrieve all results
// $this->or_query->range(0, -1);
// Add sorting.
$this->or_query->sort('search_api_relevance', 'DESC');
// Set one or more tags for the query.
// @see hook_search_api_query_TAG_alter()
// @see hook_search_api_results_TAG_alter()
$this->or_query->addTag('materio_sapi_search_or_query');
$or_results = $this->or_query->execute();
foreach ($or_results as $result) {
$nid = $result->getField('nid')->getValues()[0];
// !! have to remove duplicates instead of $exclude_and_results_conditions (solr too many boolean clauses)
if ( !in_array($nid, $this->results['nids']) ) {
$this->results['uuids'][] = $result->getField('uuid')->getValues()[0];
$this->results['nids'][] = $nid;
}
}
// todo you may like / more like this
}
private function defaultQuery(){
$lang = \Drupal::languageManager()->getCurrentLanguage()->getId();
$entity_storage = \Drupal::entityTypeManager()->getStorage('node');
$this->query = $entity_storage->getQuery()
->condition('type', ['materiau', 'thematique'], 'IN')
->condition('status', '1')
->condition('langcode', $lang)
->range($this->offset, $this->limit)
->accessCheck(TRUE)
->sort('created', 'DESC');
// ->condition('field_example', 'test_value')
$this->results = $this->query->execute();
$this->count_query = $entity_storage->getQuery()
->condition('type', ['materiau', 'thematique'], 'IN')
->condition('langcode', $lang)
->accessCheck(TRUE)
->condition('status', '1')
->count();
$this->count = $this->count_query->execute();
}
/**
* get params from request
*/
private function parseRequest(Request $request){
// Get the typed string from the URL, if it exists.
$this->keys = $request->query->get('keys');
if($this->keys){
$this->keys = mb_strtolower($this->keys);
$this->keys = Tags::explode($this->keys);
// \Drupal::logger('materio_sapi')->notice($this->keys);
}
// get the exacte term id in case of autocomplete
// $this->terms = $request->query->get('terms');
$t = $request->query->get('terms');
// $this->terms = strlen($t) ? explode(',', $t) : null;
$this->terms = strlen($t) ? json_decode($t) : null;
// get the filters of advanced search
$f = $request->query->get('filters');
$this->filters = strlen($f) ? explode(',', $f) : null;
// $this->allparams = $request->query->all();
// $request->attributes->get('_raw_variables')->get('filters')
//
$this->offset = $request->query->get('offset') ?? $this->offset;
$this->limit = $request->query->get('limit') ?? $this->limit;
}
/**
* Handler for ajax search.
*/
public function getResults(Request $request) {
$this->parseRequest($request);
$resp = [
'range' => array(
'offset' => $this->offset,
'limit' => $this->limit
),
];
if ($this->keys || $this->filters) {
$lang = \Drupal::languageManager()->getCurrentLanguage()->getId();
$this->sapiQuery();
$resp['keys'] = json_encode($this->keys);
$resp['terms'] = json_encode($this->terms);
$resp['filters'] = $this->filters;
// $resp['count'] = $this->results->getResultCount();
$resp['count'] = count($this->results['nids']);
$resp['exactematch_count'] = $this->exactematch_count;
$resp['infos'] = t('The search found @exactmatchcount exact match result(s) for @count total result(s) with', array(
"@exactmatchcount" => $resp['exactematch_count'],
"@count" => $resp['count']
));
if ($this->keys) {
$resp['infos'] .= t(' keywords @keys', array(
"@keys" => implode(', ', $this->keys)
));
}
if ($this->keys && $this->filters) {
$resp['infos'] .= " and";
}
if ($this->filters) {
// get the terms names from tid
$storage = \Drupal::entityTypeManager()->getStorage('taxonomy_term');
$filters_names = [];
foreach ($this->filters as $tid) {
/** @var \Drupal\Core\Entity\EntityInterface $entity */
$entity = $storage->load($tid);
$entity_trans = \Drupal::service('entity.repository')->getTranslationFromContext($entity, $lang);
$filters_names[] = $entity_trans->getName();
}
$resp['infos'] .= t(' filters @filters', array(
"@filters" => implode(', ', $filters_names)
));
}
// $resp['options'] = $this->query->getOptions();
// $uuids = [];
// $nids = [];
// foreach ($this->results as $result) {
// $uuids[] = $result->getField('uuid')->getValues()[0];
// $nids[] = $result->getField('nid')->getValues()[0];
// }
$resp['nids'] = array_slice($this->results['nids'], $this->offset, $this->limit);
$resp['uuids'] = array_slice($this->results['uuids'], $this->offset, $this->limit);
} else {
// no keys or terms to search for
// display the default base page
$this->defaultQuery();
// $uuids = [];
$nids = [];
// Using entityTypeManager
// Get a node storage object.
// $node_storage = \Drupal::entityTypeManager()->getStorage('node');
foreach ($this->results as $result) {
// $lang = \Drupal::languageManager()->getCurrentLanguage()->getId();
// Load a single node.
// $node = $node_storage->load($result);
// check if has translation
// i used to filter like bellow because of a graphql probleme
// if ($node->hasTranslation($lang)) {
$nids[] = $result;
// }
}
// $resp['uuids'] = $uuids;
$resp['nids'] = $nids;
$resp['count'] = $this->count;
$resp['infos'] = t('Please use the search form to search from our @count materials.', array(
"@count" => $resp['count']
));
}
return new JsonResponse($resp);
}
/**
* Handler for regular page search.
*/
function pageHandler(Request $request){
// \Drupal::logger('materio_sapi')->notice(print_r($request, true));
$this->parseRequest($request);
$resp = [
"#title" => 'Base'
];
if ($this->keys) {
$resp['#title'] = implode(', ', $this->keys);
// $this->sapiQuery();
// $node_storage = \Drupal::entityTypeManager()->getStorage('node');
// $node_view_builder = \Drupal::entityTypeManager()->getViewBuilder('node');
// // $items = $this->results->getResultItems();
// $nids = $this->results['nids'];
// $this->items = [];
// foreach ($nids as $nid) {
// // \Drupal::logger('materio_sapi')->notice(print_r($nid, true));
// try {
// /** @var \Drupal\Core\Entity\EntityInterface $entity */
// // $entity = $item->getOriginalObject()->getValue();
// $entity = $node_storage->load($nid);
// }
// catch (SearchApiException $e) {
// continue;
// }
// if (!$entity) {
// continue;
// }
// // TODO: define dynamicly viewmode
// $this->items[] = $node_view_builder->view($entity, 'teaser');
// }
// $resp['items'] = $this->items;
$resp['items'] = [];
}else{
$resp['#markup'] = t("no keys to search for");
}
return $resp;
}
}