132cd2800SGreg Roach<?php 232cd2800SGreg Roach/** 332cd2800SGreg Roach * webtrees: online genealogy 432cd2800SGreg Roach * Copyright (C) 2019 webtrees development team 532cd2800SGreg Roach * This program is free software: you can redistribute it and/or modify 632cd2800SGreg Roach * it under the terms of the GNU General Public License as published by 732cd2800SGreg Roach * the Free Software Foundation, either version 3 of the License, or 832cd2800SGreg Roach * (at your option) any later version. 932cd2800SGreg Roach * This program is distributed in the hope that it will be useful, 1032cd2800SGreg Roach * but WITHOUT ANY WARRANTY; without even the implied warranty of 1132cd2800SGreg Roach * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 1232cd2800SGreg Roach * GNU General Public License for more details. 1332cd2800SGreg Roach * You should have received a copy of the GNU General Public License 1432cd2800SGreg Roach * along with this program. If not, see <http://www.gnu.org/licenses/>. 1532cd2800SGreg Roach */ 1632cd2800SGreg Roachdeclare(strict_types=1); 1732cd2800SGreg Roach 1832cd2800SGreg Roachnamespace Fisharebest\Webtrees\Services; 1932cd2800SGreg Roach 2032cd2800SGreg Roachuse Closure; 2132cd2800SGreg Roachuse Fisharebest\Webtrees\Family; 2232cd2800SGreg Roachuse Fisharebest\Webtrees\GedcomRecord; 2332cd2800SGreg Roachuse Fisharebest\Webtrees\Individual; 2432cd2800SGreg Roachuse Fisharebest\Webtrees\Media; 2532cd2800SGreg Roachuse Fisharebest\Webtrees\Note; 2632cd2800SGreg Roachuse Fisharebest\Webtrees\Repository; 2732cd2800SGreg Roachuse Fisharebest\Webtrees\Source; 2832cd2800SGreg Roachuse Fisharebest\Webtrees\Tree; 2932cd2800SGreg Roachuse Illuminate\Database\Capsule\Manager as DB; 3032cd2800SGreg Roachuse Illuminate\Database\Query\Builder; 3132cd2800SGreg Roachuse Illuminate\Database\Query\JoinClause; 3232cd2800SGreg Roachuse Illuminate\Support\Collection; 3332cd2800SGreg Roachuse stdClass; 3432cd2800SGreg Roach 3532cd2800SGreg Roach/** 3632cd2800SGreg Roach * Search trees for genealogy records. 3732cd2800SGreg Roach */ 3832cd2800SGreg Roachclass SearchService 3932cd2800SGreg Roach{ 4032cd2800SGreg Roach /** 4132cd2800SGreg Roach * Search for families by name. 4232cd2800SGreg Roach * 4332cd2800SGreg Roach * @param Tree $tree 4432cd2800SGreg Roach * @param string $search 4532cd2800SGreg Roach * @param int $offset 4632cd2800SGreg Roach * @param int $limit 4732cd2800SGreg Roach * 4832cd2800SGreg Roach * @return Collection|Family[] 4932cd2800SGreg Roach */ 5032cd2800SGreg Roach public function searchFamiliesByName(Tree $tree, string $search, int $offset = 0, int $limit = PHP_INT_MAX): Collection 5132cd2800SGreg Roach { 5232cd2800SGreg Roach $prefix = DB::connection()->getTablePrefix(); 5332cd2800SGreg Roach 5432cd2800SGreg Roach $query = DB::table('families') 5532cd2800SGreg Roach ->where('f_file', '=', $tree->id()) 5632cd2800SGreg Roach ->join('name AS husb_name', function (JoinClause $join) use ($search): void { 5732cd2800SGreg Roach $join 5832cd2800SGreg Roach ->on('husb_name.n_file', '=', 'families.f_file') 5932cd2800SGreg Roach ->on('husb_name.n_id', '=', 'families.f_husb') 6032cd2800SGreg Roach ->where('husb_name.n_type', '<>', '_MARNM'); 6132cd2800SGreg Roach }) 6232cd2800SGreg Roach ->join('name AS wife_name', function (JoinClause $join) use ($search): void { 6332cd2800SGreg Roach $join 6432cd2800SGreg Roach ->on('wife_name.n_file', '=', 'families.f_file') 6532cd2800SGreg Roach ->on('wife_name.n_id', '=', 'families.f_wife') 6632cd2800SGreg Roach ->where('wife_name.n_type', '<>', '_MARNM'); 6732cd2800SGreg Roach }) 6832cd2800SGreg Roach ->whereContains(DB::raw("CONCAT(" . $prefix . "husb_name.n_full, ' ', " . $prefix . "wife_name.n_full)"), $search) 6932cd2800SGreg Roach ->orderBy('husb_name.n_sort') 7032cd2800SGreg Roach ->orderBy('wife_name.n_sort') 7132cd2800SGreg Roach ->select(['families.f_id', 'families.f_gedcom', 'husb_name.n_sort', 'wife_name.n_sort']) 7232cd2800SGreg Roach ->distinct(); 7332cd2800SGreg Roach 74*886b77daSGreg Roach $row_mapper = Family::rowMapper($tree); 7532cd2800SGreg Roach 7632cd2800SGreg Roach return $this->paginateQuery($query, $row_mapper, $offset, $limit); 7732cd2800SGreg Roach } 7832cd2800SGreg Roach 7932cd2800SGreg Roach /** 8032cd2800SGreg Roach * Search for individuals by name. 8132cd2800SGreg Roach * 8232cd2800SGreg Roach * @param Tree $tree 8332cd2800SGreg Roach * @param string $search 8432cd2800SGreg Roach * @param int $offset 8532cd2800SGreg Roach * @param int $limit 8632cd2800SGreg Roach * 8732cd2800SGreg Roach * @return Collection|Individual[] 8832cd2800SGreg Roach */ 8932cd2800SGreg Roach public function searchIndividualsByName(Tree $tree, string $search, int $offset = 0, int $limit = PHP_INT_MAX): Collection 9032cd2800SGreg Roach { 9132cd2800SGreg Roach $query = DB::table('individuals') 9232cd2800SGreg Roach ->where('i_file', '=', $tree->id()) 9332cd2800SGreg Roach ->join('name', function (JoinClause $join) use ($search): void { 9432cd2800SGreg Roach $join 9532cd2800SGreg Roach ->on('name.n_file', '=', 'individuals.i_file') 9632cd2800SGreg Roach ->on('name.n_id', '=', 'individuals.i_id') 9732cd2800SGreg Roach ->whereContains('n_full', $search); 9832cd2800SGreg Roach }) 9932cd2800SGreg Roach ->select(['individuals.i_id', 'individuals.i_gedcom', 'n_sort']) 10032cd2800SGreg Roach ->distinct(); 10132cd2800SGreg Roach 102*886b77daSGreg Roach $row_mapper = Individual::rowMapper($tree); 10332cd2800SGreg Roach 10432cd2800SGreg Roach return $this->paginateQuery($query, $row_mapper, $offset, $limit); 10532cd2800SGreg Roach } 10632cd2800SGreg Roach 10732cd2800SGreg Roach /** 10832cd2800SGreg Roach * Search for media objects. 10932cd2800SGreg Roach * 11032cd2800SGreg Roach * @param Tree $tree 11132cd2800SGreg Roach * @param string $search 11232cd2800SGreg Roach * @param int $offset 11332cd2800SGreg Roach * @param int $limit 11432cd2800SGreg Roach * 11532cd2800SGreg Roach * @return Collection|Media[] 11632cd2800SGreg Roach */ 11732cd2800SGreg Roach public function searchMedia(Tree $tree, string $search, int $offset = 0, int $limit = PHP_INT_MAX): Collection 11832cd2800SGreg Roach { 11932cd2800SGreg Roach $query = DB::table('media') 12032cd2800SGreg Roach ->where('media.m_file', '=', $tree->id()) 12132cd2800SGreg Roach ->join('media_file', function (JoinClause $join) use ($search): void { 12232cd2800SGreg Roach $join 12332cd2800SGreg Roach ->on('media_file.m_file', '=', 'media.m_file') 12432cd2800SGreg Roach ->on('media_file.m_id', '=', 'media.m_id'); 12532cd2800SGreg Roach }) 12632cd2800SGreg Roach ->where(function (Builder $query) use ($search): void { 12732cd2800SGreg Roach $query 12832cd2800SGreg Roach ->whereContains('multimedia_file_refn', $search) 12932cd2800SGreg Roach ->whereContains('descriptive_title', $search, 'or'); 13032cd2800SGreg Roach }) 13132cd2800SGreg Roach ->select(['media.m_id', 'media.m_gedcom']) 13232cd2800SGreg Roach ->distinct(); 13332cd2800SGreg Roach 134*886b77daSGreg Roach $row_mapper = Media::rowMapper($tree); 13532cd2800SGreg Roach 13632cd2800SGreg Roach return $this->paginateQuery($query, $row_mapper, $offset, $limit); 13732cd2800SGreg Roach } 13832cd2800SGreg Roach 13932cd2800SGreg Roach /** 14032cd2800SGreg Roach * Search for notes. 14132cd2800SGreg Roach * 14232cd2800SGreg Roach * @param Tree $tree 14332cd2800SGreg Roach * @param string $search 14432cd2800SGreg Roach * @param int $offset 14532cd2800SGreg Roach * @param int $limit 14632cd2800SGreg Roach * 14732cd2800SGreg Roach * @return Collection|Note[] 14832cd2800SGreg Roach */ 14932cd2800SGreg Roach public function searchNotes(Tree $tree, string $search, int $offset = 0, int $limit = PHP_INT_MAX): Collection 15032cd2800SGreg Roach { 15132cd2800SGreg Roach $query = DB::table('other') 15232cd2800SGreg Roach ->where('o_file', '=', $tree->id()) 15332cd2800SGreg Roach ->where('o_type', '=', 'NOTE') 15432cd2800SGreg Roach ->whereContains('o_gedcom', $search) 15532cd2800SGreg Roach ->orderBy('o_id') 15632cd2800SGreg Roach ->select(['o_id', 'o_gedcom']); 15732cd2800SGreg Roach 158*886b77daSGreg Roach $row_mapper = Note::rowMapper($tree); 15932cd2800SGreg Roach 16032cd2800SGreg Roach return $this->paginateQuery($query, $row_mapper, $offset, $limit); 16132cd2800SGreg Roach } 16232cd2800SGreg Roach 16332cd2800SGreg Roach /** 16432cd2800SGreg Roach * Search for repositories. 16532cd2800SGreg Roach * 16632cd2800SGreg Roach * @param Tree $tree 16732cd2800SGreg Roach * @param string $search 16832cd2800SGreg Roach * @param int $offset 16932cd2800SGreg Roach * @param int $limit 17032cd2800SGreg Roach * 17132cd2800SGreg Roach * @return Collection|Repository[] 17232cd2800SGreg Roach */ 17332cd2800SGreg Roach public function searchRepositories(Tree $tree, string $search, int $offset = 0, int $limit = PHP_INT_MAX): Collection 17432cd2800SGreg Roach { 17532cd2800SGreg Roach $query = DB::table('other') 17632cd2800SGreg Roach ->where('o_file', '=', $tree->id()) 17732cd2800SGreg Roach ->where('o_type', '=', 'REPO') 17832cd2800SGreg Roach ->whereContains('o_gedcom', $search) 17932cd2800SGreg Roach ->orderBy('o_id') 18032cd2800SGreg Roach ->select(['o_id', 'o_gedcom']); 18132cd2800SGreg Roach 182*886b77daSGreg Roach $row_mapper = Repository::rowMapper($tree); 18332cd2800SGreg Roach 18432cd2800SGreg Roach return $this->paginateQuery($query, $row_mapper, $offset, $limit); 18532cd2800SGreg Roach } 18632cd2800SGreg Roach 18732cd2800SGreg Roach /** 18832cd2800SGreg Roach * Search for sources by name. 18932cd2800SGreg Roach * 19032cd2800SGreg Roach * @param Tree $tree 19132cd2800SGreg Roach * @param string $search 19232cd2800SGreg Roach * @param int $offset 19332cd2800SGreg Roach * @param int $limit 19432cd2800SGreg Roach * 19532cd2800SGreg Roach * @return Collection|Source[] 19632cd2800SGreg Roach */ 19732cd2800SGreg Roach public function searchSourcesByName(Tree $tree, string $search, int $offset = 0, int $limit = PHP_INT_MAX): Collection 19832cd2800SGreg Roach { 19932cd2800SGreg Roach $query = DB::table('sources') 20032cd2800SGreg Roach ->where('s_file', '=', $tree->id()) 20132cd2800SGreg Roach ->whereContains('s_name', $search) 20232cd2800SGreg Roach ->orderBy('s_name') 20332cd2800SGreg Roach ->select(['s_id', 's_gedcom']); 20432cd2800SGreg Roach 205*886b77daSGreg Roach $row_mapper = Source::rowMapper($tree); 20632cd2800SGreg Roach 20732cd2800SGreg Roach return $this->paginateQuery($query, $row_mapper, $offset, $limit); 20832cd2800SGreg Roach } 20932cd2800SGreg Roach 21032cd2800SGreg Roach /** 21132cd2800SGreg Roach * Search for submitters. 21232cd2800SGreg Roach * 21332cd2800SGreg Roach * @param Tree $tree 21432cd2800SGreg Roach * @param string $search 21532cd2800SGreg Roach * @param int $offset 21632cd2800SGreg Roach * @param int $limit 21732cd2800SGreg Roach * 21832cd2800SGreg Roach * @return Collection|GedcomRecord[] 21932cd2800SGreg Roach */ 22032cd2800SGreg Roach public function searchSubmitters(Tree $tree, string $search, int $offset = 0, int $limit = PHP_INT_MAX): Collection 22132cd2800SGreg Roach { 22232cd2800SGreg Roach $query = DB::table('other') 22332cd2800SGreg Roach ->where('o_file', '=', $tree->id()) 22432cd2800SGreg Roach ->where('o_type', '=', 'SUBM') 22532cd2800SGreg Roach ->whereContains('o_gedcom', $search) 22632cd2800SGreg Roach ->orderBy('o_id') 22732cd2800SGreg Roach ->select(['o_id', 'o_gedcom']); 22832cd2800SGreg Roach 229*886b77daSGreg Roach $row_mapper = GedcomRecord::rowMapper($tree); 23032cd2800SGreg Roach 23132cd2800SGreg Roach return $this->paginateQuery($query, $row_mapper, $offset, $limit); 23232cd2800SGreg Roach } 23332cd2800SGreg Roach 23432cd2800SGreg Roach /** 23532cd2800SGreg Roach * Paginate a search query. 23632cd2800SGreg Roach * 23732cd2800SGreg Roach * @param Builder $query Searches the database for the desired records. 23832cd2800SGreg Roach * @param Closure $row_mapper Converts a row from the query into a record. 23932cd2800SGreg Roach * @param int $offset Skip this many rows. 24032cd2800SGreg Roach * @param int $limit Take this many rows. 24132cd2800SGreg Roach * 24232cd2800SGreg Roach * @return Collection 24332cd2800SGreg Roach */ 24432cd2800SGreg Roach private function paginateQuery(Builder $query, Closure $row_mapper, int $offset, int $limit): Collection 24532cd2800SGreg Roach { 24632cd2800SGreg Roach $collection = new Collection(); 24732cd2800SGreg Roach 24832cd2800SGreg Roach foreach ($query->cursor() as $row) { 24932cd2800SGreg Roach $record = $row_mapper($row); 25032cd2800SGreg Roach 25132cd2800SGreg Roach if ($record->canShow()) { 25232cd2800SGreg Roach if ($offset > 0) { 25332cd2800SGreg Roach $offset--; 25432cd2800SGreg Roach } else { 25532cd2800SGreg Roach if ($limit > 0) { 25632cd2800SGreg Roach $collection->push($record); 25732cd2800SGreg Roach } 25832cd2800SGreg Roach 25932cd2800SGreg Roach $limit--; 26032cd2800SGreg Roach 26132cd2800SGreg Roach if ($limit === 0) { 26232cd2800SGreg Roach break; 26332cd2800SGreg Roach } 26432cd2800SGreg Roach } 26532cd2800SGreg Roach } 26632cd2800SGreg Roach } 26732cd2800SGreg Roach 26832cd2800SGreg Roach return $collection; 26932cd2800SGreg Roach } 27032cd2800SGreg Roach} 271