donfanning
8/15/2018 - 1:17 PM

TNT Searchable Trait

TNT Searchable Trait

<?php

use Illuminate\Database\Eloquent\Model;
use TeamTNT\TNTSearch\Facades\TNTSearch;

trait Searchable
{
    /**
     * Auto update search index using model events.
     *
     * @return void
     */
    protected static function syncSearchableIndex()
    {
        if (config('services.tntsearch.sync') && ! app()->runningInConsole()) {
            static::created([__CLASS__, 'insertToIndex']);
            static::updated([__CLASS__, 'updateIndex']);
            static::deleted([__CLASS__, 'deleteFromIndex']);
        }
    }

    /**
     * Dele a entity from the search index.
     *
     * @param  \Illuminate\Database\Eloquent\Model $entity
     *
     * @return void
     */
    public static function deleteFromIndex(Model $entity)
    {
        $entity->createIndexIfNotExists();
        TNTSearch::selectIndex($entity->searchableAs());
        $index = TNTSearch::getIndex();
        $index->delete($entity->getPrimaryKeyColumn());
    }

    /**
     * Update a entity in the search index.
     *
     * @param  \Illuminate\Database\Eloquent\Model $entity
     *
     * @return void
     */
    public static function updateIndex(Model $entity)
    {
        $entity->createIndexIfNotExists();
        TNTSearch::selectIndex($entity->searchableAs());
        $index = TNTSearch::getIndex();
        $index->update($entity->getPrimaryKeyColumn(), $entity->toSearchableArray());
    }

    /**
     * Insert a new entity in the search index.
     *
     * @param  \Illuminate\Database\Eloquent\Model $entity
     *
     * @return void
     */
    public static function insertToIndex(Model $entity)
    {
        $entity->createIndexIfNotExists();
        TNTSearch::selectIndex($entity->searchableAs());
        $index = TNTSearch::getIndex();
        $index->insert($entity->toSearchableArray());
    }

    /**
     * Create a new search index if doesn't exists one.
     *
     * @return void
     */
    public function createIndexIfNotExists()
    {
        $indexFile = sprintf('%s/%s', config('services.tntsearch.storage'), $this->searchableAs());

        if (! is_file($indexFile)) {
            TNTSearch::createIndex($this->searchableAs());
        }
    }

    /**
     * Perform a search against the model's indexed data.
     *
     * @param  \Illuminate\Database\Eloquent\Builder $builder
     * @param  string                                $search
     *
     * @return \Illuminate\Database\Eloquent\Builder
     */
    public function scopeSearch($builder, $query)
    {
        TNTSearch::selectIndex($this->searchableAs());

        $results = TNTSearch::search($query, $this->count());

        $ids = $results['ids'];
        $column = $this->getPrimaryKeyColumn();
        $orderByQuery = sprintf('FIELD (%s, %s)', $column, implode(', ', $ids));

        return $builder->whereIn($column, $ids)->orderByRaw($orderByQuery);
    }

    /**
     * Get the primary key column name.
     *
     * @return string
     */
    private function getPrimaryKeyColumn()
    {
        return sprintf('%s.%s', $this->getTable(), $this->getKeyName());
    }

    /**
     * Get the index name for the model.
     *
     * @return string
     */
    public function searchableAs()
    {
        return sprintf('%s.index', strtolower(class_basename(get_called_class())));
    }

    /**
     * Get the indexable data array for the model.
     *
     * @return array
     */
    public function toSearchableArray()
    {
        return $this->toArray();
    }
}