nickwest/EloquentForms

View on GitHub
src/Table.php

Summary

Maintainability
A
1 hr
Test Coverage
<?php

namespace Nickwest\EloquentForms;

use Illuminate\Support\Collection;
use Nickwest\EloquentForms\Exceptions\InvalidFieldException;
use Nickwest\EloquentForms\Exceptions\InvalidRouteException;
use Nickwest\EloquentForms\Exports\TableExporter;
use Nickwest\EloquentForms\Traits\Themeable;
use Route;
use View;

class Table
{
    use Themeable;

    /**
     * Submit Button name (used for first submit button only).
     *
     * @var Nickwest\EloquentForms\Attributes
     */
    public $attributes = null;

    /**
     * Collection that the table will display.
     *
     * @var Illuminate\Support\Collection
     */
    public $Collection = null;

    /**
     * Exporter used to generate Excel file.
     *
     * @var Maatwebsite\Excel\Concerns\Exportable
     */
    public $Exporter = null;

    /**
     * Fields that should not have htmlspecialchars applied.
     *
     * @var array
     */
    public $raw_fields = [];

    /**
     * Array of field names.
     *
     * @var array
     */
    protected $display_fields = [];

    /**
     * Array of field names.
     *
     * @var array
     */
    protected $field_replacements = [];

    /**
     * Theme to use.
     *
     * @var string
     */
    protected $Theme = null;

    /**
     * Array of labels, keyed by field_name.
     *
     * @var array
     */
    protected $labels = [];

    /**
     * Array of css classes.
     *
     * @var array
     */
    protected $classes = [];

    /**
     * Array of column linking patterns keyed by field_name.
     *
     * @var array
     */
    protected $linking_patterns = [];

    /**
     * Constructor.
     *
     * @return void
     */
    public function __construct()
    {
        $this->Theme = new DefaultTheme();
        $this->attributes = new Attributes();
        $this->Exporter = new TableExporter($this);
    }

    /**
     * Set the Collection data to the Table Object.
     *
     * @param  Illuminate\Support\Collection  $Collection
     * @return void
     */
    public function setData(Collection $Collection): void
    {
        $this->Collection = $Collection;
    }

    /**
     * member mutator.
     *
     * @param  array  $field_names
     * @return void
     */
    public function setDisplayFields(array $field_names): void
    {
        foreach ($field_names as $field_name) {
            $this->display_fields[$field_name] = $field_name;
        }
    }

    /**
     * Display Fields Accessor.
     *
     * @return array
     */
    public function getDisplayFields(): array
    {
        return $this->display_fields;
    }

    /**
     * Add field labels to the existing labels.
     *
     * @param  array  $labels
     * @return void
     *
     * @throws Nickwest\EloquentForms\Exceptions\InvalidFieldException
     */
    public function setLabels(array $labels): void
    {
        foreach ($labels as $field_name => $label) {
            if (isset($this->display_fields[$field_name])) {
                $this->labels[$field_name] = $label;
            } else {
                throw new InvalidFieldException('"'.$field_name.'" not set as a display field');
            }
        }
    }

    /**
     * Get a Label for a specific field.
     *
     * @param  string  $field_name
     * @return string
     */
    public function getLabel(string $field_name): string
    {
        if (isset($this->labels[$field_name])) {
            return $this->labels[$field_name];
        }

        // This should always be done the same was as Field::makeLabel()
        return ucfirst(str_replace('_', ' ', $field_name));
    }

    /**
     * Get array of Labels.
     *
     * @return array
     */
    public function getLabels(): array
    {
        return $this->labels;
    }

    /**
     * Set a replacement string for a given field's output. Use {field_name} to inject values
     * field_name supports any field set in the given object/array that exists within the Collection.
     *
     * @param  string  $field  field name
     * @param  string  $html  non-escaped text to replace field value in output
     * @return void
     */
    public function addFieldReplacement(string $field, string $html): void
    {
        $this->field_replacements[$field] = $html;
    }

    /**
     * Check if a field has a replacement pattern.
     *
     * @param  string  $field  field name
     * @return bool
     */
    public function hasFieldReplacement(string $field): bool
    {
        return isset($this->field_replacements[$field]);
    }

    /**
     * Get a field's replacement value.
     *
     * @param  string  $field  field name
     * @param  string  $Object  Object or array
     * @return string
     */
    public function getFieldReplacement(string $field, &$Object): string
    {
        $pattern = '/\{([a-zA-Z0-9_\?]+)\}/';
        $results = [];
        preg_match_all($pattern, $this->field_replacements[$field], $results, PREG_PATTERN_ORDER);

        $replaced = $this->field_replacements[$field];

        if (! is_array($results[0]) || ! is_array($results[1])) {
            return $replaced;
        }

        foreach ($results[0] as $key => $match) {
            // If it's an option parameter, drop the question market
            $optional = false;
            if (strpos($results[1][$key], '?') !== false) {
                $optional = true;
                $results[1][$key] = str_replace('?', '', $results[1][$key]);
            }

            if (is_object($Object) && isset($Object->{$results[1][$key]})) {
                $replaced = $this->fieldReplaceString($results[0][$key], $Object->{$results[1][$key]}, $replaced, $field);
            } elseif (is_array($Object) && isset($Object[$results[1][$key]])) {
                $replaced = $this->fieldReplaceString($results[0][$key], $Object[$results[1][$key]], $replaced, $field);
            } elseif ($optional) {
                $replaced = str_replace($results[0][$key], '', $replaced);
            }
        }

        return $replaced;
    }

    /**
     * Get array of Field Replacements.
     *
     * @return array
     */
    public function getFieldReplacements(): array
    {
        return $this->field_replacements;
    }

    /**
     * String replace for specific field, conditionally uses htmlspecialchars.
     *
     * @param  string  $search
     * @param  string  $replace
     * @param  string  $subject
     * @param  string  $field
     * @return string
     */
    protected function fieldReplaceString($search, $replace, $subject, $field)
    {
        if (in_array($field, $this->raw_fields)) {
            $replaced = str_replace($search, $replace, $subject);
        } else {
            $replaced = str_replace($search, e($replace), $subject);
        }

        return $replaced;
    }

    /**
     *  Convenience method for setting a linking pattern on a field.
     *
     * @param  string  $field_name
     * @param  string  $href
     */
    public function addLinkingPattern(string $field_name, string $href): void
    {
        // Make and set the linking pattern
        $this->field_replacements[$field_name] = '<a href="'.$href.'">{'.$field_name.'}</a>';
    }

    /**
     * Convenience method for creating a link replacement pattern by route name.
     *
     * @param  string  $field_name
     * @param  string  $route_name
     * @param  array  $replacement_map
     * @param  mixed  $query_string
     * @return void
     *
     * @throws Nickwest\EloquentForms\InvalidRouteException
     */
    public function addLinkingPatternByRoute(string $field_name, string $route_name, array $replacement_map = [], $query_string = []): void
    {
        $Route = Route::getRoutes()->getByName($route_name);
        if ($Route == null) {
            throw new InvalidRouteException('Invalid route name '.$route_name);
        }

        $uri = $Route->uri;
        foreach ($replacement_map as $key => $new) {
            $uri = str_replace($key, $new, $uri);
        }

        if (is_array($query_string) && count($query_string) > 0) {
            $uri .= '?'.implode('&', array_map(function ($key, $val) {
                return $key.'='.$val;
            }, array_keys($query_string), $query_string));
        } elseif (is_string($query_string)) {
            $uri .= '?'.$query_string;
        }

        // If it already has a linking pattern, replace $field_name with that linking pattern
        $link_text = (isset($this->field_replacements[$field_name]) ? $this->field_replacements[$field_name] : '{'.$field_name.'}');

        // Make and set the linking pattern
        $this->field_replacements[$field_name] = '<a href="/'.$uri.'">'.$link_text.'</a>';
    }

    /**
     * Make a view and extend $extends in $section, $blade_data is the data array to pass to View::make().
     *
     * @param  array  $blade_data
     * @param  string  $extends
     * @param  string  $section
     * @return Illuminate\View\View
     */
    public function makeView(array $blade_data = [], string $extends = '', string $section = ''): \Illuminate\View\View
    {
        $blade_data['Table'] = $this;
        $blade_data['extends'] = $extends;
        $blade_data['section'] = $section;

        $this->Theme->prepareTableView($this);

        $template = ($extends != '' ? 'table-extend' : 'table');

        return $this->getThemeView($template, $blade_data);
    }
}