/** * Filters a query object with this item's data given a model * * @param Query $query * @param Eloquent $model * * @return void */ public function filterQuery(&$query, $model) { //run the parent method parent::filterQuery($query, $model); //if there is no value, return if (!$this->value) { return; } //if the table hasn't been joined yet, join it if (!Column::isJoined($query, $this->table)) { $query->join($this->table, $model->table() . '.' . $model::$key, '=', $this->table . '.' . $this->column); } $query->where_in($this->table . '.id', is_array($this->value) ? $this->value : array($this->value)); }
/** * Helper that builds a results array (with results and pagination info) * * @param object $model * @param array $sortOptions (with 'field' and 'direction' keys) * @param array $filters (see getFilters helper for the value types) */ public static function getRows($model, $sortOptions, $filters = null) { //get the columns and sort options $columns = Column::getColumns($model, false); $sort = Sort::get($model, $sortOptions['field'], $sortOptions['direction']); //get things going by grouping the set $query = $model::group_by($model->table() . '.' . $model::$key); //set up initial array states for the selects $selects = array(DB::raw($model->table() . '.' . $model::$key), DB::raw($model->table() . '.*')); //then we set the filters if ($filters && is_array($filters)) { foreach ($filters as $filter) { if (!($fieldObject = Field::get($filter['field'], $filter, $model))) { continue; } $fieldObject->filterQuery($query, $model); } } //determines if the sort should have the table prefixed to it $sortOnTable = true; //iterate over the columns to check if we need to join any values or add any extra columns foreach ($columns['columns'] as $field => $column) { //if this is a related column, we'll need to add some joins $column->filterQuery($query, $selects, $model); //if this is a related field or if (($column->isRelated || $column->select) && $column->field === $sort->field) { $sortOnTable = false; } } //if the sort is on the model's table, prefix the table name to it if ($sortOnTable) { $sort->field = $model->table() . '.' . $sort->field; } //if there is a global per page limit set, make sure the paginator uses that $per_page = $model->per_page() ? $model->per_page() : 20; $global_per_page = Config::get('administrator::administrator.global_per_page', NULL); if ($global_per_page && is_numeric($global_per_page)) { $per_page = $global_per_page; } /** * We need to do our own pagination since there is a bug (!!!!!!!!!!!!!!) in the L3 paginator when using groupings :( * When L4 is released, this problem will go away and we'll be able to use the paginator again * Trust me, I understand how ghetto this is. I also understand that it may not work too well on other drivers. Let me know... */ //first get the sql sans selects $sql = $query->table->grammar->select($query->table); //then we need to round out the inner select $sql = "SELECT {$model->table()}.{$model::$key} " . $sql; //then wrap the inner table and perform the count $sql = "SELECT COUNT({$model::$key}) AS aggregate FROM ({$sql}) AS agg"; //then perform the count query $results = $query->table->connection->query($sql, $query->table->bindings); $num_rows = $results[0]->aggregate; $page = (int) \Input::get('page', 1); $last = (int) ceil($num_rows / $per_page); //if the current page is greater than the last page, set the current page to the last page $page = $page > $last ? $last : $page; //now we need to limit and offset the rows in remembrance of our dear lost friend paginate() $query->take($per_page); $query->skip($per_page * ($page === 0 ? $page : $page - 1)); //order the set by the model table's id $query->order_by($sort->field, $sort->direction); //then retrieve the rows $rows = $query->distinct()->get($selects); $results = array(); //convert the resulting set into arrays foreach ($rows as $item) { //iterate over the included and related columns $onTableColumns = array_merge($columns['includedColumns'], $columns['relatedColumns']); $arr = array(); foreach ($onTableColumns as $field => $col) { $arr[$field] = $item->get_attribute($field); } //then grab the computed, unsortable columns foreach ($columns['computedColumns'] as $col) { $arr[$col] = $item->{$col}; } $results[] = $arr; } return array('page' => $page, 'last' => $last, 'total' => $num_rows, 'results' => $results); }
<?php use Admin\Libraries\ModelHelper; use Admin\Libraries\Fields\Field; use Admin\Libraries\Column; use Admin\Libraries\Sort; //View Composers //admin index view View::composer('administrator::index', function ($view) { //get a model instance that we'll use for constructing stuff $modelInstance = ModelHelper::getModel($view->modelName); $columns = Column::getColumns($modelInstance); $editFields = Field::getEditFields($modelInstance); $bundleConfig = Bundle::get('administrator'); //add the view fields $view->modelTitle = Config::get('administrator::administrator.models.' . $view->modelName . '.title', $view->modelName); $view->modelSingle = Config::get('administrator::administrator.models.' . $view->modelName . '.single', $view->modelTitle); $view->columns = $columns['columns']; $view->includedColumns = $columns['includedColumns']; $view->primaryKey = $modelInstance::$key; $view->sort = Sort::get($modelInstance)->toArray(); $view->rows = ModelHelper::getRows($modelInstance, $view->sort); $view->editFields = $editFields['arrayFields']; $view->dataModel = $editFields['dataModel']; $view->filters = ModelHelper::getFilters($modelInstance); $view->baseUrl = URL::to_route('admin_index'); $view->bundleHandles = $bundleConfig['handles']; $view->expandWidth = ModelHelper::getExpandWidth($modelInstance); $view->modelInstance = $modelInstance; $view->model = isset($view->model) ? $view->model : false; });
/** * The constructor takes a field, column array, and the associated Eloquent model * * @param array $config //the array of options provide in each model's config */ public function __construct($config) { //set the class properties for the items which we know to exist $this->title = array_get($config, 'title'); $this->single = array_get($config, 'single'); $this->model = array_get($config, 'model'); $this->columns = array_get($config, 'columns'); $this->actions = array_get($config, 'actions'); $this->edit = array_get($config, 'edit_fields'); $this->filters = array_get($config, 'filters', array()); $this->name = array_get($config, 'model_name'); //fetch the meaningful information for columns and actions //we won't do the same for edit fields and filters because that information is not always persistent across a request $this->columns = Column::getColumns($this); $this->actions = Action::getActions($this); //copy $this->model because of php syntax issues $model = $this->model; //now set the properties for other items //form width option $formWidth = array_get($config, 'form_width', $this->formWidth); if (!is_int($formWidth) || $formWidth < $this->formWidth) { $formWidth = $this->formWidth; } $this->formWidth = $formWidth; //sort options $this->sort = array_get($config, 'sort', array()); $this->setSort(); //get the rows per page $this->setRowsPerPage(); //grab the model link callback $linkCallback = array_get($config, 'link'); $this->linkCallback = is_callable($linkCallback) ? $linkCallback : null; //grab the action permissions, if supplied $actionPermissions = array_get($config, 'action_permissions', array()); $create = array_get($actionPermissions, 'create'); $delete = array_get($actionPermissions, 'delete'); $update = array_get($actionPermissions, 'update'); $this->actionPermissions['create'] = is_callable($create) ? $create() : true; $this->actionPermissions['delete'] = is_callable($delete) ? $delete() : true; $this->actionPermissions['update'] = is_callable($update) ? $update() : true; }
/** * Constrains a query by a given set of constraints * * @param Query $query * @param Eloquent $model * @param array $constraints * * @return void */ public function constrainQuery(&$query, $model, $constraints) { //if the column hasn't been joined yet, join it if (!Column::isJoined($query, $this->table)) { $query->join($this->table, $model->table() . '.' . $model::$key, '=', $this->column2); } $query->where($this->column, '=', $constraints); }
/** * Gets a model's columns given the a model's config * * @param ModelConfig $config * * @return array( * 'columns' => array(detailed..), * 'includedColumns' => array(field => full_column_name, ...)), * 'computedColumns' => array(key, key, key) */ public static function getColumns($config) { $model = $config->model; $return = array('columns' => array(), 'columnArrays' => array(), 'columnObjects' => array(), 'includedColumns' => array(), 'computedColumns' => array(), 'relatedColumns' => array()); //check if there are columns to iterate over if (count($config->columns) > 0) { $columns = array(); foreach ($config->columns as $field => $column) { //get the column object if (!($columnObject = Column::get($field, $column, $config))) { continue; } //save the column object with a $field-based key, as a simple array (to use in knockout), and as a simple array of arrays $return['columnObjects'][$field] = $columnObject; $return['columns'][] = $columnObject; $return['columnArrays'][] = $columnObject->toArray(); //categorize the columns if ($columnObject->isRelated) { $return['relatedColumns'][$columnObject->field] = $columnObject->field; if ($fk = $columnObject->relationshipField->foreignKey) { $return['includedColumns'][$fk] = $model->table() . '.' . $fk; } } else { if ($columnObject->isComputed) { $return['computedColumns'][$columnObject->field] = $columnObject->field; } else { $return['includedColumns'][$columnObject->field] = $model->table() . '.' . $columnObject->field; } } } } else { throw new Exception("Administrator: you must provide a valid 'columns' array in each model's config"); } //make sure the table key is included if (!array_get($return['includedColumns'], $model::$key)) { $return['includedColumns'][$model::$key] = $model->table() . '.' . $model::$key; } return $return; }
/** * Gets a model's columns given the a model's config * * @param ModelConfig $config * * @return array( * 'columns' => array(detailed..), * 'includedColumns' => array(field => full_column_name, ...)), * 'computedColumns' => array(key, key, key) */ public static function getColumns($config) { $model = $config->model; $return = array('columns' => array(), 'columnArrays' => array(), 'columnObjects' => array(), 'includedColumns' => array(), 'computedColumns' => array(), 'relatedColumns' => array()); //check if there are columns to iterate over if (count($config->columns) > 0) { $columns = array(); foreach ($config->columns as $field => $column) { //get the column object if (!($columnObject = Column::get($field, $column, $config))) { continue; } //save the column object with a $field-based key, as a simple array (to use in knockout), and as a simple array of arrays $return['columnObjects'][$field] = $columnObject; $return['columns'][] = $columnObject; $return['columnArrays'][] = $columnObject->toArray(); //categorize the columns if ($columnObject->isRelated) { $return['relatedColumns'][$columnObject->field] = $columnObject->field; //if there are nested values, we'll want to grab the values slightly differently if (sizeof($columnObject->nested)) { $fk = $columnObject->nested['models'][0]->{$columnObject->nested['pieces'][0]}()->foreign; $return['includedColumns'][$fk] = $model->table() . '.' . $fk; } else { if ($fk = $columnObject->relationshipField->foreignKey) { $return['includedColumns'][$fk] = $model->table() . '.' . $fk; } } } else { if ($columnObject->isComputed) { $return['computedColumns'][$columnObject->field] = $columnObject->field; } else { $return['includedColumns'][$columnObject->field] = $model->table() . '.' . $columnObject->field; } } } } else { throw new Exception("Administrator: you must provide a valid 'columns' array in each model's config"); } //make sure the table key is included if (!array_get($return['includedColumns'], $model::$key)) { $return['includedColumns'][$model::$key] = $model->table() . '.' . $model::$key; } //make sure any belongs_to fields that aren't on the columns list are included $editFields = Field::getEditFields($config); foreach ($editFields['objectFields'] as $field => $info) { if (is_a($info, 'Admin\\Libraries\\Fields\\Relationships\\BelongsTo')) { $return['includedColumns'][$info->foreignKey] = $model->table() . '.' . $info->foreignKey; } } return $return; }
/** * Gets the model's columns * * @param object $model * @param bool $toArray * * @return array( * 'columns' => array(detailed..), * 'includedColumns' => array(field => full_column_name, ...)), * 'computedColumns' => array(key, key, key) */ public static function getColumns($model, $toArray = true) { $return = array('columns' => array(), 'includedColumns' => array(), 'computedColumns' => array(), 'relatedColumns' => array()); if (isset($model->columns) && count($model->columns) > 0) { $columns = array(); foreach ($model->columns as $field => $column) { //get the column object if (!($columnObject = Column::get($field, $column, $model))) { continue; } //if $toArray is true, add the column as an array. otherwise add the column object if ($toArray) { $return['columns'][$columnObject->field] = $columnObject->toArray(); } else { $return['columns'][$columnObject->field] = $columnObject; } //categorize the columns if ($columnObject->isRelated) { $return['relatedColumns'][$columnObject->field] = $columnObject->field; if ($fk = $columnObject->relationshipField->foreignKey) { $return['includedColumns'][$fk] = $model->table() . '.' . $fk; } } else { if ($columnObject->isComputed) { $return['computedColumns'][$columnObject->field] = $columnObject->field; } else { $return['includedColumns'][$columnObject->field] = $model->table() . '.' . $columnObject->field; } } } } else { //throw exception! } //make sure the table key is included if (!array_get($return['includedColumns'], $model::$key)) { $return['includedColumns'][$model::$key] = $model->table() . '.' . $model::$key; } return $return; }