xref: /webtrees/app/Module/PlaceHierarchyListModule.php (revision 7413816e6dd2d50e569034fb804f3dce7471bb94)
167992b6aSRichard Cissee<?php
23976b470SGreg Roach
367992b6aSRichard Cissee/**
467992b6aSRichard Cissee * webtrees: online genealogy
5d11be702SGreg Roach * Copyright (C) 2023 webtrees development team
667992b6aSRichard Cissee * This program is free software: you can redistribute it and/or modify
767992b6aSRichard Cissee * it under the terms of the GNU General Public License as published by
867992b6aSRichard Cissee * the Free Software Foundation, either version 3 of the License, or
967992b6aSRichard Cissee * (at your option) any later version.
1067992b6aSRichard Cissee * This program is distributed in the hope that it will be useful,
1167992b6aSRichard Cissee * but WITHOUT ANY WARRANTY; without even the implied warranty of
1267992b6aSRichard Cissee * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1367992b6aSRichard Cissee * GNU General Public License for more details.
1467992b6aSRichard Cissee * You should have received a copy of the GNU General Public License
1589f7189bSGreg Roach * along with this program. If not, see <https://www.gnu.org/licenses/>.
1667992b6aSRichard Cissee */
17fcfa147eSGreg Roach
1867992b6aSRichard Cisseedeclare(strict_types=1);
1967992b6aSRichard Cissee
2067992b6aSRichard Cisseenamespace Fisharebest\Webtrees\Module;
2167992b6aSRichard Cissee
2267992b6aSRichard Cisseeuse Fisharebest\Webtrees\Auth;
236f4ec3caSGreg Roachuse Fisharebest\Webtrees\DB;
24c8db8a43SGreg Roachuse Fisharebest\Webtrees\Family;
250e54db38SGreg Roachuse Fisharebest\Webtrees\Http\RequestHandlers\MapDataEdit;
2667992b6aSRichard Cisseeuse Fisharebest\Webtrees\I18N;
27c8db8a43SGreg Roachuse Fisharebest\Webtrees\Individual;
28c8db8a43SGreg Roachuse Fisharebest\Webtrees\Location;
294b2f1dbbSGreg Roachuse Fisharebest\Webtrees\Place;
304b2f1dbbSGreg Roachuse Fisharebest\Webtrees\PlaceLocation;
31158900c2SGreg Roachuse Fisharebest\Webtrees\Registry;
32c9c6f2ecSGreg Roachuse Fisharebest\Webtrees\Services\LeafletJsService;
334b2f1dbbSGreg Roachuse Fisharebest\Webtrees\Services\ModuleService;
344b2f1dbbSGreg Roachuse Fisharebest\Webtrees\Services\SearchService;
355229eadeSGreg Roachuse Fisharebest\Webtrees\Tree;
36b55cbc6bSGreg Roachuse Fisharebest\Webtrees\Validator;
37a020b8bdSGreg Roachuse Illuminate\Database\Query\Builder;
38a020b8bdSGreg Roachuse Illuminate\Database\Query\JoinClause;
396ccdf4f0SGreg Roachuse Psr\Http\Message\ResponseInterface;
406ccdf4f0SGreg Roachuse Psr\Http\Message\ServerRequestInterface;
414b2f1dbbSGreg Roachuse Psr\Http\Server\RequestHandlerInterface;
42f3874e19SGreg Roach
434b2f1dbbSGreg Roachuse function array_chunk;
444b2f1dbbSGreg Roachuse function array_pop;
454b2f1dbbSGreg Roachuse function array_reverse;
464b2f1dbbSGreg Roachuse function ceil;
474b2f1dbbSGreg Roachuse function count;
484b2f1dbbSGreg Roachuse function redirect;
494b2f1dbbSGreg Roachuse function route;
504b2f1dbbSGreg Roachuse function view;
5167992b6aSRichard Cissee
5267992b6aSRichard Cissee/**
5367992b6aSRichard Cissee * Class IndividualListModule
5467992b6aSRichard Cissee */
554b2f1dbbSGreg Roachclass PlaceHierarchyListModule extends AbstractModule implements ModuleListInterface, RequestHandlerInterface
5667992b6aSRichard Cissee{
5767992b6aSRichard Cissee    use ModuleListTrait;
5867992b6aSRichard Cissee
590e54db38SGreg Roach    protected const ROUTE_URL = '/tree/{tree}/place-list{/place_id}';
604b2f1dbbSGreg Roach
614b2f1dbbSGreg Roach    /** @var int The default access level for this module.  It can be changed in the control panel. */
6233c746f1SGreg Roach    protected int $access_level = Auth::PRIV_USER;
634b2f1dbbSGreg Roach
64c9c6f2ecSGreg Roach    private LeafletJsService $leaflet_js_service;
65c9c6f2ecSGreg Roach
66c9c6f2ecSGreg Roach    private ModuleService $module_service;
67c9c6f2ecSGreg Roach
68c9c6f2ecSGreg Roach    private SearchService $search_service;
694b2f1dbbSGreg Roach
704b2f1dbbSGreg Roach    /**
71c9c6f2ecSGreg Roach     * @param LeafletJsService $leaflet_js_service
72c9c6f2ecSGreg Roach     * @param ModuleService    $module_service
734b2f1dbbSGreg Roach     * @param SearchService    $search_service
744b2f1dbbSGreg Roach     */
75c9c6f2ecSGreg Roach    public function __construct(LeafletJsService $leaflet_js_service, ModuleService $module_service, SearchService $search_service)
764b2f1dbbSGreg Roach    {
77c9c6f2ecSGreg Roach        $this->leaflet_js_service = $leaflet_js_service;
78c9c6f2ecSGreg Roach        $this->module_service     = $module_service;
794b2f1dbbSGreg Roach        $this->search_service     = $search_service;
804b2f1dbbSGreg Roach    }
814b2f1dbbSGreg Roach
824b2f1dbbSGreg Roach    /**
834b2f1dbbSGreg Roach     * Initialization.
844b2f1dbbSGreg Roach     *
854b2f1dbbSGreg Roach     * @return void
864b2f1dbbSGreg Roach     */
874b2f1dbbSGreg Roach    public function boot(): void
884b2f1dbbSGreg Roach    {
89158900c2SGreg Roach        Registry::routeFactory()->routeMap()
904b2f1dbbSGreg Roach            ->get(static::class, static::ROUTE_URL, $this);
914b2f1dbbSGreg Roach    }
924b2f1dbbSGreg Roach
9367992b6aSRichard Cissee    /**
940cfd6963SGreg Roach     * How should this module be identified in the control panel, etc.?
9567992b6aSRichard Cissee     *
9667992b6aSRichard Cissee     * @return string
9767992b6aSRichard Cissee     */
9867992b6aSRichard Cissee    public function title(): string
9967992b6aSRichard Cissee    {
10067992b6aSRichard Cissee        /* I18N: Name of a module/list */
10167992b6aSRichard Cissee        return I18N::translate('Place hierarchy');
10267992b6aSRichard Cissee    }
10367992b6aSRichard Cissee
10467992b6aSRichard Cissee    public function description(): string
10567992b6aSRichard Cissee    {
1069e0868cbSGreg Roach        /* I18N: Description of the “Place hierarchy” module */
10767992b6aSRichard Cissee        return I18N::translate('The place hierarchy.');
10867992b6aSRichard Cissee    }
10967992b6aSRichard Cissee
11067992b6aSRichard Cissee    /**
11167992b6aSRichard Cissee     * CSS class for the URL.
11267992b6aSRichard Cissee     *
11367992b6aSRichard Cissee     * @return string
11467992b6aSRichard Cissee     */
11567992b6aSRichard Cissee    public function listMenuClass(): string
11667992b6aSRichard Cissee    {
11767992b6aSRichard Cissee        return 'menu-list-plac';
11867992b6aSRichard Cissee    }
11967992b6aSRichard Cissee
1204db4b4a9SGreg Roach    /**
12124f2a3afSGreg Roach     * @return array<string>
1224db4b4a9SGreg Roach     */
12367992b6aSRichard Cissee    public function listUrlAttributes(): array
12467992b6aSRichard Cissee    {
12567992b6aSRichard Cissee        return [];
12667992b6aSRichard Cissee    }
1274b2f1dbbSGreg Roach
1284b2f1dbbSGreg Roach    /**
1294b2f1dbbSGreg Roach     * @param Tree $tree
1304b2f1dbbSGreg Roach     *
1314b2f1dbbSGreg Roach     * @return bool
1324b2f1dbbSGreg Roach     */
1334b2f1dbbSGreg Roach    public function listIsEmpty(Tree $tree): bool
1344b2f1dbbSGreg Roach    {
1354b2f1dbbSGreg Roach        return !DB::table('places')
1364b2f1dbbSGreg Roach            ->where('p_file', '=', $tree->id())
1374b2f1dbbSGreg Roach            ->exists();
1384b2f1dbbSGreg Roach    }
1394b2f1dbbSGreg Roach
1404b2f1dbbSGreg Roach    /**
141c9c6f2ecSGreg Roach     * @param Tree                                      $tree
14276d39c55SGreg Roach     * @param array<bool|int|string|array<string>|null> $parameters
143c9c6f2ecSGreg Roach     *
144c9c6f2ecSGreg Roach     * @return string
145c9c6f2ecSGreg Roach     */
146c9c6f2ecSGreg Roach    public function listUrl(Tree $tree, array $parameters = []): string
147c9c6f2ecSGreg Roach    {
148c9c6f2ecSGreg Roach        $parameters['tree'] = $tree->name();
149c9c6f2ecSGreg Roach
150c9c6f2ecSGreg Roach        return route(static::class, $parameters);
151c9c6f2ecSGreg Roach    }
152c9c6f2ecSGreg Roach
153c9c6f2ecSGreg Roach    /**
1544b2f1dbbSGreg Roach     * @param ServerRequestInterface $request
1554b2f1dbbSGreg Roach     *
1564b2f1dbbSGreg Roach     * @return ResponseInterface
1574b2f1dbbSGreg Roach     */
1584b2f1dbbSGreg Roach    public function handle(ServerRequestInterface $request): ResponseInterface
1594b2f1dbbSGreg Roach    {
160b55cbc6bSGreg Roach        $tree     = Validator::attributes($request)->tree();
161b55cbc6bSGreg Roach        $user     = Validator::attributes($request)->user();
1620e54db38SGreg Roach        $place_id = Validator::attributes($request)->integer('place_id', 0);
1634b2f1dbbSGreg Roach
1644b2f1dbbSGreg Roach        Auth::checkComponentAccess($this, ModuleListInterface::class, $tree, $user);
1654b2f1dbbSGreg Roach
166748dbe15SGreg Roach        $action2  = Validator::queryParams($request)->string('action2', 'hierarchy');
1674b2f1dbbSGreg Roach        $place    = Place::find($place_id, $tree);
1684b2f1dbbSGreg Roach
1694b2f1dbbSGreg Roach        // Request for a non-existent place?
1704b2f1dbbSGreg Roach        if ($place_id !== $place->id()) {
1714b2f1dbbSGreg Roach            return redirect($place->url());
1724b2f1dbbSGreg Roach        }
1734b2f1dbbSGreg Roach
174c9c6f2ecSGreg Roach        $map_providers = $this->module_service->findByInterface(ModuleMapProviderInterface::class);
175c9c6f2ecSGreg Roach
1764b2f1dbbSGreg Roach        $content = '';
177c9c6f2ecSGreg Roach        $showmap = $map_providers->isNotEmpty();
1784b2f1dbbSGreg Roach        $data    = null;
1794b2f1dbbSGreg Roach
1804b2f1dbbSGreg Roach        if ($showmap) {
1814b2f1dbbSGreg Roach            $content .= view('modules/place-hierarchy/map', [
182edfa2139SGreg Roach                'data'           => $this->mapData($place),
183c9c6f2ecSGreg Roach                'leaflet_config' => $this->leaflet_js_service->config(),
1844b2f1dbbSGreg Roach            ]);
1854b2f1dbbSGreg Roach        }
1864b2f1dbbSGreg Roach
1874b2f1dbbSGreg Roach        switch ($action2) {
1884b2f1dbbSGreg Roach            case 'list':
1894b2f1dbbSGreg Roach            default:
1904b2f1dbbSGreg Roach                $alt_link = I18N::translate('Show place hierarchy');
1914b2f1dbbSGreg Roach                $alt_url  = $this->listUrl($tree, ['action2' => 'hierarchy', 'place_id' => $place_id]);
1924b2f1dbbSGreg Roach                $content .= view('modules/place-hierarchy/list', ['columns' => $this->getList($tree)]);
1934b2f1dbbSGreg Roach                break;
1944b2f1dbbSGreg Roach            case 'hierarchy':
1954b2f1dbbSGreg Roach            case 'hierarchy-e':
1964b2f1dbbSGreg Roach                $alt_link = I18N::translate('Show all places in a list');
1974b2f1dbbSGreg Roach                $alt_url  = $this->listUrl($tree, ['action2' => 'list', 'place_id' => 0]);
1984b2f1dbbSGreg Roach                $data     = $this->getHierarchy($place);
19961999471SGreg Roach                $content .= ($data === null || $showmap) ? '' : view('place-hierarchy', $data);
20061999471SGreg Roach                if ($data === null || $action2 === 'hierarchy-e') {
2014b2f1dbbSGreg Roach                    $content .= view('modules/place-hierarchy/events', [
2024b2f1dbbSGreg Roach                        'indilist' => $this->search_service->searchIndividualsInPlace($place),
2034b2f1dbbSGreg Roach                        'famlist'  => $this->search_service->searchFamiliesInPlace($place),
2044b2f1dbbSGreg Roach                        'tree'     => $place->tree(),
2054b2f1dbbSGreg Roach                    ]);
2064b2f1dbbSGreg Roach                }
2074b2f1dbbSGreg Roach        }
2084b2f1dbbSGreg Roach
2094b2f1dbbSGreg Roach        if ($data !== null && $action2 !== 'hierarchy-e' && $place->gedcomName() !== '') {
2104b2f1dbbSGreg Roach            $events_link = $this->listUrl($tree, ['action2' => 'hierarchy-e', 'place_id' => $place_id]);
2114b2f1dbbSGreg Roach        } else {
2124b2f1dbbSGreg Roach            $events_link = '';
2134b2f1dbbSGreg Roach        }
2144b2f1dbbSGreg Roach
2154b2f1dbbSGreg Roach        $breadcrumbs = $this->breadcrumbs($place);
2164b2f1dbbSGreg Roach
2174b2f1dbbSGreg Roach        return $this->viewResponse('modules/place-hierarchy/page', [
2184b2f1dbbSGreg Roach            'alt_link'    => $alt_link,
2194b2f1dbbSGreg Roach            'alt_url'     => $alt_url,
2204b2f1dbbSGreg Roach            'breadcrumbs' => $breadcrumbs['breadcrumbs'],
2214b2f1dbbSGreg Roach            'content'     => $content,
2224b2f1dbbSGreg Roach            'current'     => $breadcrumbs['current'],
2234b2f1dbbSGreg Roach            'events_link' => $events_link,
2244b2f1dbbSGreg Roach            'place'       => $place,
2254b2f1dbbSGreg Roach            'title'       => I18N::translate('Place hierarchy'),
2264b2f1dbbSGreg Roach            'tree'        => $tree,
227c9c6f2ecSGreg Roach            'world_url'   => $this->listUrl($tree),
2284b2f1dbbSGreg Roach        ]);
2294b2f1dbbSGreg Roach    }
2304b2f1dbbSGreg Roach
2314b2f1dbbSGreg Roach    /**
2320e54db38SGreg Roach     * @param Place $place
2334b2f1dbbSGreg Roach     *
2344b2f1dbbSGreg Roach     * @return array<mixed>
2354b2f1dbbSGreg Roach     */
2360e54db38SGreg Roach    protected function mapData(Place $place): array
2374b2f1dbbSGreg Roach    {
2380e54db38SGreg Roach        $children  = $place->getChildPlaces();
2394b2f1dbbSGreg Roach        $features  = [];
2404b2f1dbbSGreg Roach        $sidebar   = '';
2414b2f1dbbSGreg Roach        $show_link = true;
2424b2f1dbbSGreg Roach
2430e54db38SGreg Roach        // No children?  Show ourself on the map instead.
2440e54db38SGreg Roach        if ($children === []) {
2450e54db38SGreg Roach            $children[] = $place;
2464b2f1dbbSGreg Roach            $show_link  = false;
2474b2f1dbbSGreg Roach        }
2484b2f1dbbSGreg Roach
2490e54db38SGreg Roach        foreach ($children as $id => $child) {
2500e54db38SGreg Roach            $location = new PlaceLocation($child->gedcomName());
2510e54db38SGreg Roach
2520e54db38SGreg Roach            if (Auth::isAdmin()) {
2530e54db38SGreg Roach                $this_url = route(self::class, ['tree' => $child->tree()->name(), 'place_id' => $place->id()]);
2540e54db38SGreg Roach                $edit_url = route(MapDataEdit::class, ['location_id' => $location->id(), 'url' => $this_url]);
2550e54db38SGreg Roach            } else {
2560e54db38SGreg Roach                $edit_url = '';
2570e54db38SGreg Roach            }
2584b2f1dbbSGreg Roach
25990949315SGreg Roach            if ($location->latitude() === null || $location->longitude() === null) {
2604b2f1dbbSGreg Roach                $sidebar_class = 'unmapped';
2614b2f1dbbSGreg Roach            } else {
2624b2f1dbbSGreg Roach                $sidebar_class = 'mapped';
2634b2f1dbbSGreg Roach                $features[]    = [
2644b2f1dbbSGreg Roach                    'type'       => 'Feature',
2654b2f1dbbSGreg Roach                    'id'         => $id,
2664b2f1dbbSGreg Roach                    'geometry'   => [
2674b2f1dbbSGreg Roach                        'type'        => 'Point',
2684b2f1dbbSGreg Roach                        'coordinates' => [$location->longitude(), $location->latitude()],
2694b2f1dbbSGreg Roach                    ],
2704b2f1dbbSGreg Roach                    'properties' => [
2710e54db38SGreg Roach                        'tooltip' => $child->gedcomName(),
2724b2f1dbbSGreg Roach                        'popup'   => view('modules/place-hierarchy/popup', [
2730e54db38SGreg Roach                            'edit_url'  => $edit_url,
2740e54db38SGreg Roach                            'place'     => $child,
2754b2f1dbbSGreg Roach                            'latitude'  => $location->latitude(),
2764b2f1dbbSGreg Roach                            'longitude' => $location->longitude(),
2770e54db38SGreg Roach                            'showlink'  => $show_link,
2784b2f1dbbSGreg Roach                        ]),
2794b2f1dbbSGreg Roach                    ],
2804b2f1dbbSGreg Roach                ];
2814b2f1dbbSGreg Roach            }
2824b2f1dbbSGreg Roach
283a020b8bdSGreg Roach            $stats = [
2840e54db38SGreg Roach                Family::RECORD_TYPE     => $this->familyPlaceLinks($child)->count(),
2850e54db38SGreg Roach                Individual::RECORD_TYPE => $this->individualPlaceLinks($child)->count(),
2860e54db38SGreg Roach                Location::RECORD_TYPE   => $this->locationPlaceLinks($child)->count(),
287a020b8bdSGreg Roach            ];
2884b2f1dbbSGreg Roach
2894b2f1dbbSGreg Roach            $sidebar .= view('modules/place-hierarchy/sidebar', [
2900e54db38SGreg Roach                'edit_url'      => $edit_url,
2914b2f1dbbSGreg Roach                'id'            => $id,
2920e54db38SGreg Roach                'place'         => $child,
2930e54db38SGreg Roach                'showlink'      => $show_link,
2944b2f1dbbSGreg Roach                'sidebar_class' => $sidebar_class,
295c8db8a43SGreg Roach                'stats'         => $stats,
2964b2f1dbbSGreg Roach            ]);
2974b2f1dbbSGreg Roach        }
2984b2f1dbbSGreg Roach
2994b2f1dbbSGreg Roach        return [
3000e54db38SGreg Roach            'bounds'  => (new PlaceLocation($place->gedcomName()))->boundingRectangle(),
3014b2f1dbbSGreg Roach            'sidebar' => $sidebar,
3024b2f1dbbSGreg Roach            'markers' => [
3034b2f1dbbSGreg Roach                'type'     => 'FeatureCollection',
3044b2f1dbbSGreg Roach                'features' => $features,
305c9c6f2ecSGreg Roach            ],
306c9c6f2ecSGreg Roach        ];
307c9c6f2ecSGreg Roach    }
308c9c6f2ecSGreg Roach
309c9c6f2ecSGreg Roach    /**
310c9c6f2ecSGreg Roach     * @param Tree $tree
311c9c6f2ecSGreg Roach     *
312c9c6f2ecSGreg Roach     * @return array<array<Place>>
313c9c6f2ecSGreg Roach     */
314c9c6f2ecSGreg Roach    private function getList(Tree $tree): array
315c9c6f2ecSGreg Roach    {
316c9c6f2ecSGreg Roach        $places = $this->search_service->searchPlaces($tree, '')
317f25fc0f9SGreg Roach            ->sort(static fn (Place $x, Place $y): int => I18N::comparator()($x->gedcomName(), $y->gedcomName()))
318c9c6f2ecSGreg Roach            ->all();
319c9c6f2ecSGreg Roach
320c9c6f2ecSGreg Roach        $count = count($places);
321c9c6f2ecSGreg Roach
322c9c6f2ecSGreg Roach        if ($places === []) {
323c9c6f2ecSGreg Roach            return [];
324c9c6f2ecSGreg Roach        }
325c9c6f2ecSGreg Roach
326c9c6f2ecSGreg Roach        $columns = $count > 20 ? 3 : 2;
327c9c6f2ecSGreg Roach
328c9c6f2ecSGreg Roach        return array_chunk($places, (int) ceil($count / $columns));
329c9c6f2ecSGreg Roach    }
330c9c6f2ecSGreg Roach
331c9c6f2ecSGreg Roach    /**
332c9c6f2ecSGreg Roach     * @param Place $place
333c9c6f2ecSGreg Roach     *
334bd29d468SGreg Roach     * @return array{columns:array<array<Place>>,place:Place,tree:Tree,col_class:string}|null
335c9c6f2ecSGreg Roach     */
336*1ff45046SGreg Roach    private function getHierarchy(Place $place): array|null
337c9c6f2ecSGreg Roach    {
338c9c6f2ecSGreg Roach        $child_places = $place->getChildPlaces();
339c9c6f2ecSGreg Roach        $numfound     = count($child_places);
340c9c6f2ecSGreg Roach
341c9c6f2ecSGreg Roach        if ($numfound > 0) {
342c9c6f2ecSGreg Roach            $divisor = $numfound > 20 ? 3 : 2;
343c9c6f2ecSGreg Roach
344c9c6f2ecSGreg Roach            return [
345c9c6f2ecSGreg Roach                'tree'      => $place->tree(),
346c9c6f2ecSGreg Roach                'col_class' => 'w-' . ($divisor === 2 ? '25' : '50'),
347c9c6f2ecSGreg Roach                'columns'   => array_chunk($child_places, (int) ceil($numfound / $divisor)),
348c9c6f2ecSGreg Roach                'place'     => $place,
349c9c6f2ecSGreg Roach            ];
350c9c6f2ecSGreg Roach        }
351c9c6f2ecSGreg Roach
352c9c6f2ecSGreg Roach        return null;
353c9c6f2ecSGreg Roach    }
354c9c6f2ecSGreg Roach
355c9c6f2ecSGreg Roach    /**
356c9c6f2ecSGreg Roach     * @param Place $place
357c9c6f2ecSGreg Roach     *
358bd29d468SGreg Roach     * @return array{breadcrumbs:array<Place>,current:Place|null}
359c9c6f2ecSGreg Roach     */
360c9c6f2ecSGreg Roach    private function breadcrumbs(Place $place): array
361c9c6f2ecSGreg Roach    {
362c9c6f2ecSGreg Roach        $breadcrumbs = [];
363c9c6f2ecSGreg Roach        if ($place->gedcomName() !== '') {
364c9c6f2ecSGreg Roach            $breadcrumbs[] = $place;
365c9c6f2ecSGreg Roach            $parent_place  = $place->parent();
366c9c6f2ecSGreg Roach            while ($parent_place->gedcomName() !== '') {
367c9c6f2ecSGreg Roach                $breadcrumbs[] = $parent_place;
368c9c6f2ecSGreg Roach                $parent_place  = $parent_place->parent();
369c9c6f2ecSGreg Roach            }
370c9c6f2ecSGreg Roach            $breadcrumbs = array_reverse($breadcrumbs);
371c9c6f2ecSGreg Roach            $current     = array_pop($breadcrumbs);
372c9c6f2ecSGreg Roach        } else {
373c9c6f2ecSGreg Roach            $current = null;
374c9c6f2ecSGreg Roach        }
375c9c6f2ecSGreg Roach
376c9c6f2ecSGreg Roach        return [
377c9c6f2ecSGreg Roach            'breadcrumbs' => $breadcrumbs,
378c9c6f2ecSGreg Roach            'current'     => $current,
3794b2f1dbbSGreg Roach        ];
3804b2f1dbbSGreg Roach    }
381a020b8bdSGreg Roach
382a020b8bdSGreg Roach    /**
383a020b8bdSGreg Roach     * @param Place $place
384a020b8bdSGreg Roach     *
385a020b8bdSGreg Roach     * @return Builder
386a020b8bdSGreg Roach     */
387a020b8bdSGreg Roach    private function placeLinks(Place $place): Builder
388a020b8bdSGreg Roach    {
389a020b8bdSGreg Roach        return DB::table('places')
390a020b8bdSGreg Roach            ->join('placelinks', static function (JoinClause $join): void {
391a020b8bdSGreg Roach                $join
392a020b8bdSGreg Roach                    ->on('pl_file', '=', 'p_file')
393a020b8bdSGreg Roach                    ->on('pl_p_id', '=', 'p_id');
394a020b8bdSGreg Roach            })
395a020b8bdSGreg Roach            ->where('p_file', '=', $place->tree()->id())
396a020b8bdSGreg Roach            ->where('p_id', '=', $place->id());
397a020b8bdSGreg Roach    }
398a020b8bdSGreg Roach
399a020b8bdSGreg Roach    /**
400a020b8bdSGreg Roach     * @param Place $place
401a020b8bdSGreg Roach     *
402a020b8bdSGreg Roach     * @return Builder
403a020b8bdSGreg Roach     */
404a020b8bdSGreg Roach    private function familyPlaceLinks(Place $place): Builder
405a020b8bdSGreg Roach    {
406a020b8bdSGreg Roach        return $this->placeLinks($place)
407a020b8bdSGreg Roach            ->join('families', static function (JoinClause $join): void {
408a020b8bdSGreg Roach                $join
409a020b8bdSGreg Roach                    ->on('pl_file', '=', 'f_file')
410a020b8bdSGreg Roach                    ->on('pl_gid', '=', 'f_id');
411a020b8bdSGreg Roach            });
412a020b8bdSGreg Roach    }
413a020b8bdSGreg Roach
414a020b8bdSGreg Roach    /**
415a020b8bdSGreg Roach     * @param Place $place
416a020b8bdSGreg Roach     *
417a020b8bdSGreg Roach     * @return Builder
418a020b8bdSGreg Roach     */
419a020b8bdSGreg Roach    private function individualPlaceLinks(Place $place): Builder
420a020b8bdSGreg Roach    {
421a020b8bdSGreg Roach        return $this->placeLinks($place)
422a020b8bdSGreg Roach            ->join('individuals', static function (JoinClause $join): void {
423a020b8bdSGreg Roach                $join
424a020b8bdSGreg Roach                    ->on('pl_file', '=', 'i_file')
425a020b8bdSGreg Roach                    ->on('pl_gid', '=', 'i_id');
426a020b8bdSGreg Roach            });
427a020b8bdSGreg Roach    }
428a020b8bdSGreg Roach
429a020b8bdSGreg Roach    /**
430a020b8bdSGreg Roach     * @param Place $place
431a020b8bdSGreg Roach     *
432a020b8bdSGreg Roach     * @return Builder
433a020b8bdSGreg Roach     */
434a020b8bdSGreg Roach    private function locationPlaceLinks(Place $place): Builder
435a020b8bdSGreg Roach    {
436a020b8bdSGreg Roach        return $this->placeLinks($place)
437a020b8bdSGreg Roach            ->join('other', static function (JoinClause $join): void {
438a020b8bdSGreg Roach                $join
439a020b8bdSGreg Roach                    ->on('pl_file', '=', 'o_file')
440a020b8bdSGreg Roach                    ->on('pl_gid', '=', 'o_id');
441a020b8bdSGreg Roach            })
442a020b8bdSGreg Roach            ->where('o_type', '=', Location::RECORD_TYPE);
443a020b8bdSGreg Roach    }
44467992b6aSRichard Cissee}
445