src/Traits/ControllerTrait.php
<?php
namespace HnhDigital\LaravelModelFilter\Traits;
use HnhDigital\LaravelModelFilter\Objects\SearchViewOptions;
use HnhDigital\LaravelModelFilter\Objects\SearchViewResult;
use Html;
use Illuminate\Http\Request as HttpRequest;
use Illuminate\Pagination\Paginator;
use Request;
use Route;
trait ControllerTrait
{
/**
* Get current search options.
*
* @param mixed $use_session
* @param array $settings
* @param array $options
* @param array $search_name
*
* @return mixed
*/
public function getCurrentSearchDetails($use_session, $settings, $options, $search_name)
{
if (request()->ajax()) {
$settings_data = request()->all();
} else {
$settings_data = (array) $settings;
}
foreach ($settings_data as $key => $value) {
$options[str_replace('-', '_', $key)] = $value;
}
extract($options);
$options['route_paramater'] = (empty($options['route_paramater'])) ? $current_model : $options['route_paramater'];
$options['route_prefix'] = (empty($options['route_prefix'])) ? array_get($config, 0, '') : $options['route_prefix'];
// Remove plural to get route paramater
if (substr($options['route_paramater'], -1, 1) == 's') {
$options['route_paramater'] = substr($options['route_paramater'], 0, -1);
}
// Current model
if (!isset($options['model'])) {
$options['model'] = Route::current()->parameter($options['route_paramater']);
}
$options['model_id'] = (isset($options['model'])) ? $options['model']->getKey() : '';
$options['route_name'] = (request()->ajax()) ? request()->get('route') : Route::current()->getName();
$options['route_name'] = (isset($settings['route-name'])) ? $settings['route-name'] : $options['route_name'];
(!empty($options['search_tab'])) ?: $options['search_tab'] = (!isset($settings['search-tab'])) ? request()->get('search-tab') : $settings['search-tab'];
$options['method_source'] = (!isset($settings['method-source'])) ? request()->get('method-source') : $settings['method-source'];
$options['method_source'] = (empty($options['method_source'])) ? $current_model : $options['method_source'];
// Use provided variables or use defaults
$options['attached_tab'] = (isset($attached_tab)) ? $attached_tab : str_plural($search_name, 2);
$options['attached_button_color'] = (isset($attached_button_color)) ? $attached_button_color : 'danger';
$options['attached_button_icon'] = (isset($attached_button_icon)) ? $attached_button_icon : 'times';
$options['attached_button_name'] = (isset($attached_button_name)) ? $attached_button_name : 'Remove '.str_plural($search_name, 1);
$options['unattached_tab'] = (isset($unattached_tab)) ? $unattached_tab : 'available-'.str_plural($search_name, 2);
$options['unattached_button_color'] = (isset($unattached_button_color)) ? $unattached_button_color : 'primary';
$options['unattached_button_icon'] = (isset($unattached_button_icon)) ? $unattached_button_icon : 'plus';
$options['unattached_button_name'] = (isset($unattached_button_name)) ? $unattached_button_name : 'Add '.str_plural($search_name, 1);
// Get filters from session or ajax request
$options['search_request'] = $this->getSearchAppliedFilters($current_model.'.'.$search_name.'.search', $options['model_id'].$options['search_tab'], $use_session);
(isset($options['search_request']['filters'])) ?: $options['search_request']['filters'] = [];
if (isset($settings) && is_array($settings)) {
foreach ($settings as $key => $value) {
$options['search_request'][$key] = $value;
}
}
return $options;
}
/**
* Get current search query from options or use standard.
*
* @param array $options
*
* @return mixed
*/
public function getCurrentSearchQuery($options, $class_name, $model_filter = false)
{
$class_name = 'App\\Models\\'.$class_name;
extract($options);
$route_name = (!isset($route_name)) ? $current_model : $route_name;
$other_model = (!isset($other_model)) ? camel_case($current_model) : $other_model;
// Build query
if ($search_tab == $attached_tab) {
if (isset($attached_allocations)) {
$query = $attached_allocations;
} else {
if (isset($attached_method_source)) {
return $model->$attached_method_source();
}
if (isset($override_attached_method_source)) {
$method_source = $override_attached_method_source;
}
$other_model = new $class_name();
$query = $this->getRelationQuery($model, $other_model, $method_source);
if (method_exists($query, 'onlyActive')) {
$query = $query->onlyActive();
}
}
if (isset($attached_model_filter) && $attached_model_filter instanceof \Closure) {
$query = $attached_model_filter($query);
}
if ($model_filter instanceof \Closure) {
$query = $model_filter($query);
}
} elseif ($search_tab == $unattached_tab) {
if (isset($unattached_allocations)) {
$query = $unattached_allocations;
} else {
if (isset($unattached_method_source)) {
return $model->$unattached_method_source();
}
if (isset($override_unattached_method_source)) {
$method_source = $override_unattached_method_source;
}
$other_model = new $class_name();
$list = $this->getRelationQuery($model, $other_model, $method_source)->select($other_model->getTable().'.'.$other_model->getKeyName())->pluck($other_model->getKeyName())->all();
$query = $class_name::whereNotIn($other_model->getTable().'.'.$other_model->getKeyName(), $list);
if (method_exists($query, 'onlyActive')) {
$query = $query->onlyActive();
}
}
if (isset($unattached_model_filter) && $unattached_model_filter instanceof \Closure) {
$query = $unattached_model_filter($query);
}
if ($model_filter instanceof \Closure) {
$query = $model_filter($query);
}
} else {
return ['filters' => [], 'rows' => ''];
}
return $query;
}
/**
* Get the relation query.
*
* @param mixed $model
* @param string $method_source
*
* @return
*/
private function getRelationQuery($model, $other_model, $method_source)
{
$method_name = camel_case($method_source);
if (method_exists($other_model, $method_name.'s')) {
$method_name .= 's';
}
$relation = $other_model->$method_name();
$relation_class = basename(str_replace('\\', '/', get_class($relation)));
switch ($relation_class) {
case 'BelongsTo':
$model_key_name = $relation->getForeignKey();
break;
case 'HasOne':
$model_key_name = $relation->getForeignKeyName();
break;
case 'BelongsToMany':
$method = 'getQualifiedRelatedPivotKeyName';
// < L5.5
if (method_exists($relation, 'getQualifiedRelatedKeyName')) {
$method = 'getQualifiedRelatedKeyName';
}
$model_key_name = $relation->$method();
break;
}
$model_id = $model->getKey();
return $other_model->whereHas($method_name, function ($sub_query) use ($model_key_name, $model_id) {
$sub_query->where($model_key_name, $model_id);
});
}
/**
* Default skip and take to limit queries.
*
* @var int
*/
protected static $default_paginate_items = 20;
protected static $default_take_options = [20, 50, 250, 500, 'all'];
/**
* Get the filters from the ajax request or from the session.
*
* @param string $controller_name
* @param mixed $use_session
*
* @return array
*/
protected static function getSearchAppliedFilters($controller_name, $distinct_session = '', $use_session = false)
{
// Filter provided.
if (is_array($use_session)) {
static::defaultPagination($use_session);
(isset($use_session['filters'])) ?: $use_session['filters'] = [];
return $use_session;
} elseif (request()->ajax() && !is_bool($use_session)) {
$use_session = false;
}
$distinct_session = (!empty($distinct_session)) ? '_'.$distinct_session : '';
$session_name = str_replace(['.', '-'], '_', $controller_name.'_search_filters'.$distinct_session);
$session = session($session_name);
// Remote request
if (request()->ajax() && $use_session === false) {
$provided_session = request::all();
static::defaultPagination($provided_session);
(isset($provided_session['saved_filter'])) ? $provided_session['saved_filter'] = $session['saved_filter'] : false;
(isset($provided_session['filters'])) ?: $provided_session['filters'] = [];
return $provided_session;
}
static::defaultPagination($session);
(isset($session['filters'])) ?: $session['filters'] = [];
if (!is_null(request()->filters)) {
foreach (request()->filters as $key => $value) {
$session['filters'][$key] = [$value];
}
}
// Get the session saved filters
return $session;
}
/**
* Set the default skip & take.
*
* @param array &$search_request
*
* @return void
*/
public static function defaultPagination(&$search_request)
{
if (!isset($search_request['paginate_items']) || empty($search_request['paginate_items'])) {
$search_request['paginate_items'] = static::$default_paginate_items;
}
if (request::get('page', false) !== false) {
$search_request['page'] = request::get('page');
} elseif (!isset($search_request['page'])) {
$search_request['page'] = 1;
}
$current_page = $search_request['page'];
Paginator::currentPageResolver(function () use ($current_page) {
return $current_page;
});
}
/**
* Show the filters that have been applied for this search.
*
* @param string $model
* @param \Tag &$tbody
* @param array $search_request
* @param int $column_span
*
* @return void
*/
protected static function showSearchAppliedFilters(&$tbody, &$search_request, $result, $model, $column_span = 1)
{
self::pagination($result, $search_request);
// Applied filters
if (count($search_request)) {
$filters = (new $model())->getAppliedFiltersArray($search_request['filters']);
//if (property_exists($model, 'mode_active')) {
// if (array_has($search_request, 'mode') && array_get($search_request, 'mode') > $model::$mode_active) {
// $mode_name = (array_get($search_request, 'mode') == $model::$mode_archived) ? 'archived' : 'removed';
// $filters[] = '<strong>'.$model::getFilterModelName().'</strong> <em>is</em> <strong>'.$mode_name.'</strong>';
// }
//}
if (count($filters) || isset($search_request['saved_filter']) || isset($search_request['page'])) {
$row_html = '';
if (count($filters)) {
$row_html .= 'Filtering by: '.implode('; ', $filters).'. ';
}
if ($search_request['page'] > 0 && $search_request['paginate_total'] > 0 && $search_request['paginate_last_page'] > 1) {
$row_html .= 'Showing page '.$search_request['paginate_current_page'].' of '.$search_request['paginate_last_page'].'. ';
if ($search_request['paginate_last_page'] > 1) {
$row_html .= $search_request['paginate_per_page'].' records per page.';
}
}
if (count($filters) && isset($search_request['saved_filter'])) {
$row_html .= 'Active saved filter - <strong class="saved-filter-name">'.$search_request['saved_filter']['name'].'</strong>.';
}
if (count($filters)) {
$config = array_get($search_request, 'config.5');
$button_html = '';
if (!isset($config['tab.hide']) && !isset($config['tab.advanced.show'])) {
$button_html .= Html::a()->text('%s %s', Html::icon('save'), 'Save')->addClass('btn btn-sm btn-info action-save-filter')->href('#action-save-filter')->data('toggle', 'modal').' ';
$button_html .= Html::a()->text('%s %s', Html::icon('save'), 'Change')->addClass('btn btn-sm btn-success action-change-filter')->scriptLink('Change').' ';
}
$button_html .= Html::a()->text('%s %s', Html::icon('ban'), 'Clear')->addClass('btn btn-sm btn-warning action-cancel-filter')->scriptLink('Cancel');
$row_html .= Html::div($button_html)->addClass('pull-right');
}
if (!empty($row_html)) {
$tr = $tbody->tr();
$tr->td(['colspan' => $column_span], $row_html);
}
}
}
}
/**
* Get the pagination values.
*
* @return void
*/
private static function pagination(&$result, &$search_request)
{
$search_request['paginate_count'] = $result->count();
if (method_exists($result, 'currentPage')) {
$search_request['paginate_current_page'] = $result->currentPage();
$search_request['paginate_has_more_pages'] = $result->hasMorePages();
$search_request['paginate_last_page'] = $result->lastPage();
$search_request['paginate_per_page'] = $result->perPage();
$search_request['paginate_total'] = $result->total();
} else {
$search_request['page'] = 0;
$search_request['paginate_current_page'] = 1;
$search_request['paginate_has_more_pages'] = false;
$search_request['paginate_last_page'] = 1;
$search_request['paginate_per_page'] = 1;
$search_request['paginate_total'] = $result->count();
}
}
/**
* Show no results available.
*
* @param \Tag &$tbody
* @param int $count
* @param array $search_request
* @param string $name
* @param int $column_span
* @param array $config
*
* @return void
*/
protected static function checkSearchResults($table, $result, &$search_request, $name, $column_span = 1, $config = [])
{
self::pagination($result, $search_request);
$thead = false;
if (is_array($tbody = $table)) {
list($thead, $tbody) = $table;
}
if (isset($search_request['paginate_total']) && $search_request['paginate_total'] == 0 || count($result) == 0) {
if (isset($search_request['filters']) && count($search_request['filters'])) {
$template = 'No <strong>%s</strong> can be found with the <strong>applied filters</strong>';
if (isset($config['attached_with_filter_no_results']) && $config['search_tab'] == $config['attached_tab']) {
$template = $config['attached_with_filter_no_results'];
} elseif (isset($config['unattached_with_filter_no_results']) && $config['search_tab'] != $config['attached_tab']) {
$template = $config['unattached_with_filter_no_results'];
} elseif (isset($config['with_filter_no_results'])) {
$template = $config['with_filter_no_results'];
}
} else {
$template = 'No <strong>%s</strong> exist';
if (isset($config['attached_no_filter_no_results']) && $config['search_tab'] == $config['attached_tab']) {
$template = $config['attached_no_filter_no_results'];
} elseif (isset($config['unattached_no_filter_no_results']) && $config['search_tab'] != $config['attached_tab']) {
$template = $config['unattached_no_filter_no_results'];
} elseif (isset($config['no_filter_no_results'])) {
$template = $config['no_filter_no_results'];
}
}
$row_html = sprintf($template, $name);
$tr = $tbody->tr();
$tr->td(
['colspan' => $column_span, 'style' => 'line-height: 50px;text-align:center;'],
(string) Html::span($row_html.'.', ['style' => ''])
);
}
$thead = ($thead !== false) ? $thead->prepare(['ignore_tags' => 'thead']) : '';
$tbody = $tbody->prepare(['ignore_tags' => 'tbody']);
// Prepare suitable result
return [
'thead' => $thead,
'tbody' => $tbody,
];
}
/**
* Save the current search to session.
*
* @param string $controller_name
* @param string $distinct_session
* @param array $response
*
* @return void
*/
protected static function setSearchAppliedFilters($controller_name, $distinct_session, $response)
{
$distinct_session = (!empty($distinct_session)) ? '_'.$distinct_session : '';
$session_name = str_replace(['.', '-'], '_', $controller_name.'_search_filters'.$distinct_session);
session([$session_name => $response]);
}
/**
* Update response and return.
*
* @param string $controller_name
* @param string $distinct_session
* @param array $search_request
* @param array $response
* @param bool|array $extra_response
*
* @return array
*/
protected static function returnSearchResult($controller_name, $distinct_session, $search_request, $response, $extra_response = false)
{
unset($search_request['rows']);
// Save filters to session
static::setSearchAppliedFilters($controller_name, $distinct_session, $search_request);
if ($search_request !== false) {
$response['search'] = $search_request;
if ($response['search']['paginate_total'] > 0) {
$response['count'] = $response['search']['paginate_total'].' '.str_plural('result', $response['search']['paginate_total']).', '.$response['search']['paginate_last_page'].' '.str_plural('page', $response['search']['paginate_last_page']);
} else {
$response['count'] = 'No results found.';
}
$response['total_records'] = $response['search']['paginate_total'];
$response['left_arrow'] = true;
$response['left_arrow_page'] = 0;
$response['right_arrow'] = true;
$response['right_arrow_page'] = 0;
if ($response['search']['paginate_current_page'] == 1) {
$response['left_arrow'] = false;
$response['left_arrow_page'] = 0;
} elseif ($response['search']['paginate_current_page'] > 1) {
$response['left_arrow_page'] = $response['search']['paginate_current_page'] - 1;
}
if ($response['search']['paginate_last_page'] == $response['search']['paginate_current_page'] || $response['search']['paginate_last_page'] === 0) {
$response['right_arrow'] = false;
$response['right_arrow_page'] = 0;
} elseif ($response['search']['paginate_has_more_pages']) {
$response['right_arrow_page'] = $response['search']['paginate_current_page'] + 1;
}
$response['items_per_page'] = '';
foreach (static::$default_take_options as $value) {
$response['items_per_page'] .= '<li>'.Html::a()->scriptLink('Show '.$value)->data('mode', $value)->text('Show '.$value).'</li>';
}
}
if ($extra_response instanceof \Closure) {
$response = $extra_response($response);
}
return $response;
}
/**
* Load a filter and return search results.
*
* @param AppModel $model
* @param ModelFilter $model_filter
*
* @return array
*/
public function loadFilter(HttpRequest $request, ModelFilter $model_filter)
{
$model_name = 'App\\Models\\'.$request->get('model');
$method_name = $request->get('method');
$current_filters = ((array) json_decode($model_filter->filter));
request()->replace(['filters' => $current_filters]);
// Get search results
$result = $this->$method_name();
$result['advanced_filters'] = '';
$result['lookup'] = '';
$result['search']['saved_filter'] = [
'uuid' => $model_filter->uuid,
'name' => $model_filter->name,
'description' => $model_filter->description,
'is_public' => $model_filter->is_public,
];
// Allocate lookup value
if (isset($current_filters['lookup'][0][1])) {
$result['lookup'] = $current_filters['lookup'][0][1];
}
// Generate the added filters
$model = (new $model_name());
$filter_types = $model->getFilterTypes();
$filters = $model->getFilterAttributes();
foreach ($filter_types as $type) {
$operator_options[$type] = $model->getFilterOperators($type);
}
$list_filter_options = [];
$filter_options = [['', '']];
foreach ($filters as $filter_name => $filter_settings) {
if (!isset($filter_settings['search_tab_only']) && $filter_name !== 'models') {
$filter_options[] = [$filter_name.'|'.$filter_settings['filter'], $filter_settings['name']];
}
if ($filter_settings['filter'] == 'list') {
$method = $filter_settings['method'];
$list_filter_options[$filter_name] = $model->$method();
}
}
// Load any saved filters
$original_model_filter_options = ModelFilter::active()->get();
$model_filter_options[] = ['', ''];
foreach ($original_model_filter_options as $filter_option) {
$model_filter_options[] = [$filter_option->uuid, $filter_option->name];
}
foreach ($current_filters as $filter_name => $applied_filters) {
$filter_settings = ['search' => ''];
if (isset($filters[$filter_name])) {
$filter_settings = $filters[$filter_name];
}
if (!empty($filter_settings)) {
$placeholder__count = 0;
foreach ($applied_filters as $filter) {
if (empty($filter_settings['search_tab_only'])) {
$result['advanced_filters'] .= view('common.module.content.search.filter', [
'placeholder_id' => $filter_name.'_'.$placeholder__count,
'type' => $filter_settings['filter'],
'template' => false,
'filter' => $filter,
'filter_name' => $filter_name,
'filter_settings' => $filter_settings,
'operator_options' => $operator_options[$filter_settings['filter']], ]
);
$placeholder__count++;
}
}
}
}
return $result;
}
/**
* Run a standard sub search.
*
* @param array &$view_data
* @param array $config
* @param Model $model
*
* @return void
*/
public function runStandardSubSearch(&$view_data, $config, $model)
{
foreach ($config as $config_entry) {
list($page, $name, $variable, $method, $class, $view_settings, $search_settings) = array_pad($config_entry, 7, null);
$variable .= '_search';
$method .= 'Search';
$search_settings['config'] = $config_entry;
$search_result = $this->$method(false, $search_settings);
$view_data[$variable] = [
'result' => (new SearchViewResult()),
'setup' => (new SearchViewOptions()),
'settings' => $search_settings,
];
$view_data[$variable]['result']->setArray($search_result);
$search_request_path = array_has($view_settings, 'search-request-path') ? array_get($view_settings, 'search-request-path') : '/'.$page.'/';
if (!array_get($view_settings, 'no_uuid', false)) {
if (stripos($search_request_path, '{id}') !== false) {
$search_request_path = str_replace('{id}', $model->uuid, $search_request_path);
$page = str_replace('{id}', $model->uuid, $page);
} else {
$search_request_path .= $model->uuid.'/';
}
}
if (!array_has($view_settings, 'search-request-path')) {
$search_request_path .= $method.'Result';
}
$view_data[$variable]['setup']
->set('search.layout-style', 'inline')
->set('search.name', $name.'-search')
->set('search.search_request', $search_request_path)
->set('search.controller', static::class)
->set('search.base', $page)
->set('search.method', $method)
->set('search.model', $class)
->set('search.filters', array_get($search_result, 'search.filters', []))
->set('tab.search.title', 'Results')
->set('tab.advanced.show', true)
->set('results.name', $name)
->set('colgroup.total', 1)
->set('search.show', true)
->set('search.0.text', Html::createElement('input')->name('lookup_value1[]')->type('text')->placeholder('Search by name + enter')->addClass('input form-control search-filter-field')->value(isset($search_result['search']['filters']['lookup']) ? $search_result['search']['filters']['lookup'][0][1] : ''));
foreach ((array) $view_settings as $key => $value) {
$view_data[$variable]['setup']->set($key, $value);
}
}
}
}