jon48/webtrees-lib

View on GitHub
app/Module/GeoDispersion/Services/PlacesReferenceTableService.php

Summary

Maintainability
A
2 hrs
Test Coverage
<?php

/**
 * webtrees-lib: MyArtJaub library for webtrees
 *
 * @package MyArtJaub\Webtrees
 * @subpackage GeoDispersion
 * @author Jonathan Jaubart <dev@jaubart.com>
 * @copyright Copyright (c) 2021-2022, Jonathan Jaubart
 * @license http://www.gnu.org/licenses/gpl.html GNU General Public License, version 3
 */

declare(strict_types=1);

namespace MyArtJaub\Webtrees\Module\GeoDispersion\Services;

use Illuminate\Database\Capsule\Manager as DB;

/**
 * Service for accessing data in the Places Mapping Reference Table.
 */
class PlacesReferenceTableService
{
    /**
     * Mapping format placeholder tags => table column names
     * @var array<string, string>
     */
    private const COLUMN_MAPPING = [
        'name'  =>  'majgr_place_name',
        'id'    =>  'majgr_place_admin_id',
        'zip'   =>  'majgr_place_zip',
        'gov'   =>  'majgr_place_gov_id',
        'mls'   =>  'majgr_place_mls_id'
    ];

    /**
     * Get the formatted target mapping value of a place defined by a source format.
     *
     * @param string $source
     * @param string $source_format
     * @param string $target_format
     * @return string|NULL
     */
    public function targetId(string $source, string $source_format, string $target_format): ?string
    {
        // Extract parts for the WHERE clause
        $source_format = str_replace(['{', '}'], ['{#', '#}'], $source_format);
        $source_parts = preg_split('/[{}]/i', $source_format);
        if ($source_parts === false) {
            return null;
        }
        $source_parts = array_map(function (string $part): string {
            if (preg_match('/^#([^#]+)#$/i', $part, $column_id) === 1) {
                return $this->columnName($column_id[1]);
            }
            return $this->sanitizeString(str_replace(['?', '*'], ['_', '%'], $part));
        }, array_filter($source_parts));
        $source_parts[] = "'%'";
        $concat_statement = 'CONCAT(' . implode(', ', $source_parts) . ')';

        // Extract columns used in target
        $columns = [];
        if (preg_match_all('/{(.*?)}/i', $target_format, $columns_select) === 1) {
            $columns = array_unique(array_filter(array_map(fn($id) => $this->columnName($id), $columns_select[1])));
        }

        // Get the mapping
        $rows = DB::table('maj_geodata_ref')  //DB::table('maj_geodata_ref')
            ->select($columns)
            ->whereRaw($this->sanitizeString($source) . " LIKE " . $concat_statement)
            ->get();

        // Format the output ID
        if ($rows->count() === 0) {
            return null;
        }

        $mapping = (array) $rows->first();
        if (count($columns_select) === 0) {
            return $target_format;
        }

        return str_replace(
            array_map(fn($tag) => '{' . $tag . '}', $columns_select[1]),
            array_map(fn($tag) => $mapping[$this->columnName($tag)] ?? '', $columns_select[1]),
            $target_format
        );
    }

    /**
     * Get the column name for a format placeholder tag
     *
     * @param string $placeholder
     * @return string
     */
    private function columnName(string $placeholder): string
    {
        return self::COLUMN_MAPPING[$placeholder] ?? '';
    }

    /**
     * Sanitize string for use in a SQL query.
     *
     * @param string $string
     * @return string
     */
    private function sanitizeString(string $string): string
    {
        return DB::connection()->getPdo()->quote($string);
    }
}