public function detectCallableForField(FieldInterface $field) { $method = null; $type = null; if (!$field instanceof DbField) { return false; } if ($field->isType('reference')) { return $this->handleDbReference($field, 'reference'); } elseif ($field->isType('manytomany')) { return $this->handleDbReference($field, 'manytomany'); } if ($field->isType('boolean')) { $type = 'boolean'; } elseif ($field->isType('date', 'timestamp')) { $type = 'date'; } elseif ($field->isType('integer', 'float')) { $type = 'numeric'; } elseif ($field->isType('clob', 'text')) { $type = 'text'; } else { return false; } if (null !== $type) { return function () use($type) { return array('type' => $type, 'options' => ''); }; } return false; }
/** * Adjust the behavior of the supplied field in the context of the supplied * table renderer so that it draws a "handle" that the user can grab to * drag the row around. Adds some custom JS/CSS to pull that off. * * @param FieldInterface $field * @param TableCell $cellRenderer * @param string $primaryKeyName * @return void */ public function assignToField(FieldInterface $field, TableCell $cellRenderer, $primaryKeyName) { $this->view->headScript()->appendFile($this->view->bowerUrl('/dewdrop/www/js/listing-sortable.js')); $this->view->headLink()->appendStylesheet($this->view->bowerUrl('/dewdrop/www/css/listing-sortable.css')); $cellRenderer->getContentRenderer()->assign($field->getId(), function ($helper, array $rowData) use($primaryKeyName) { /* @var $helper TableCell\Content */ return sprintf('<span data-id="%d" class="handle glyphicon glyphicon-align-justify"></span>', $helper->getView()->escapeHtmlAttr($rowData[$primaryKeyName])); }); }
/** * For DB fields, we can automatically return an Input. * * @param FieldInterface $field * @return bool|callable|mixed */ public function detectCallableForField(FieldInterface $field) { if (!$field instanceof DbField) { return false; } return function ($helper) use($field) { return $field->getInputFilter(); }; }
private function renderContent(FieldInterface $field, array $rowData, callable $outputFilter = null) { if (!isset($rowData[$field->getName()]) || !$rowData[$field->getName()]) { return null; } $date = new DateTimeImmutable($rowData[$field->getName()]); $output = $date->format($this->format); if ($outputFilter) { $output = $outputFilter($output); } return $output; }
public function __invoke(FieldInterface $field) { $field->assignHelperCallback('SelectFilter.DefaultVars', function () { $end = $this->startDate->add($this->dateInterval); // Swap dates if end if before start if ($end < $this->startDate) { return ['comp' => 'on-or-between', 'end' => $this->startDate->format('Y-m-d'), 'start' => $end->format('Y-m-d')]; } else { return ['comp' => 'on-or-between', 'start' => $this->startDate->format('Y-m-d'), 'end' => $end->format('Y-m-d')]; } }); }
private function valueChanged() { if ($this->field instanceof DbField && !$this->field->hasRow()) { return false; } $newValue = $this->field->getValue(); if (is_array($this->originalValue) && is_array($newValue)) { $intersection = count(array_intersect($this->originalValue, $newValue)); return count($this->originalValue) !== $intersection || count($newValue) !== $intersection; } else { return $this->originalValue !== $newValue; } }
public function __invoke(FieldInterface $field) { $field->addHelperFilter('TableCell.Content', function ($content, TableCell $helper, array $rowData, $rowIndex, $columnIndex) { if (!$content) { return $content; } return $helper->getView()->escapeHtml(sprintf($this->template, $content)); }); $field->addHelperFilter('CsvCell.Content', function ($content, CsvCell $helper, array $rowData, $rowIndex, $columnIndex) { if (!$content) { return $content; } return sprintf($this->template, $content); }); }
/** * Sort the listing in both ascending and descending order for the * supplied field, catching any exceptions if the sorted query causes * a problem. * * @param FieldInterface $field * @return array */ public function runOnSingleField(FieldInterface $field) { $directions = ['ASC', 'DESC']; $results = []; foreach ($directions as $direction) { $results[$direction] = array('success' => false, 'message' => null, 'sql' => ''); $request = new Request(array(), array($this->listing->getPrefix() . 'sort' => urldecode($field->getQueryStringId()), $this->listing->getPrefix() . 'dir' => $direction)); $this->sortHelper->setRequest($request); try { // Don't need much data here, just need to run the query. $select = $this->listing->getModifiedSelect($this->fields)->limit(1); $results[$direction]['sql'] = (string) $select->reset(Select::LIMIT_COUNT); $select->getAdapter()->fetchAll($select); $results[$direction]['success'] = true; } catch (CorePhpException $e) { $results[$direction]['message'] = $e->getMessage(); } } return $results; }
public function __invoke(FieldInterface $field) { $field->addHelperFilter('TableCell.Content', function ($content, TableCell $helper, array $rowData, $rowIndex, $columnIndex) { if (!$content) { return null; } if (!$this->urlTemplate || !isset($rowData[$this->id]) || !$rowData[$this->id]) { return $content; } else { $node = new Node('a'); $node->setHtml($content)->setAttribute('href', sprintf($this->urlTemplate, $rowData[$this->id]))->setAttribute('class', implode(' ', $this->cssClasses)); if ($this->title) { $node->setAttribute('title', $this->title); } if ($this->target) { $node->setAttribute('target', $this->target); } return $node; } }); }
/** * Build a Fields object that can be used when rendering the grouped counts. Supplies a field * to render the actual HTML value from the original Fields object used when fetching data for * the listing and a field to render the count for that value. We don't do any escaping here * because we assume that the original Fields object handled that in its rendering code. The * fields object returned from this method supports both TableCell and CsvCell rendering. * * @return Fields */ public function buildRenderFields() { $fields = new Fields(); $fields->add('content')->setLabel($this->groupField->getLabel())->setVisible(true)->assignHelperCallback('TableCell.Content', function (TableCell\Content $helper, array $rowData) { // Not escaping here because we assume it was escaped by the original renderer in fetchData() return $rowData['content']; })->assignHelperCallback('CsvCell.Content', function (CsvCell\Content $helper, array $rowData) { return $rowData['content']; })->add('count')->setLabel('Count')->setVisible(true)->assignHelperCallback('TableCell.Content', function (TableCell\Content $helper, array $rowData) { return $helper->getView()->escapeHtml($rowData['count']); })->assignHelperCallback('CsvCell.Content', function (CsvCell\Content $helper, array $rowData) { return $rowData['count']; }); return $fields; }
/** * Detect which view helper should be used to edit the supplied * \Dewdrop\Db\Field. THis is basic logic used to determine a suitable * helper: * * <ol> * <li> * If a custom helper was assigned by calling customizeField(), * use that. * </li> * <li> * If it is an EAV field, use whatever helper is assigned in the * EAV definition. * </li> * <li> * Otherwise, look at the field's type to determine which helper * would be appropriate. * </li> * <li> * If a suitable helper cannot be determined, throw an exception. * </li> * </ol> * * @throws \Dewdrop\Exception * @param Field $field * @return string */ public function detect(FieldInterface $field) { if (array_key_exists($field->getControlName(), $this->customHelpers)) { return $this->customHelpers[$field->getControlName()]; } elseif ($field instanceof EavField) { return $field->getEditHelperName(); } elseif ($field->isType('boolean', 'boolean')) { return 'inputCheckbox'; } elseif ($field->isType('manytomany')) { return 'checkboxList'; } elseif ($field->isType('reference')) { return 'select'; } elseif ($field->isType('clob')) { return 'textarea'; } elseif ($field->isType('text', 'integer', 'float')) { return 'inputText'; } elseif ($field->isType('date')) { return 'inputDate'; } elseif ($field->isType('timestamp')) { return 'inputTimestamp'; } throw new Exception('Fields\\EditHelperDetector: Could not find a suitable view helper for field ' . $field->getControlName() . '.'); }
/** * Wrap a field's callback to ensure that a reference to the helper is * always supplied as the first argument to the callback. * * @param callable $callable * @param FieldInterface $field * @return callable */ protected function wrapCallable(callable $callable, FieldInterface $field = null) { return function () use($callable, $field) { $arguments = func_get_args(); array_unshift($arguments, $this); $output = call_user_func_array($callable, $arguments); if ($field) { /* @var $filter callable */ foreach ($field->getHelperFilters($this->name) as $filter) { $filterArguments = $arguments; array_unshift($filterArguments, $output); $output = call_user_func_array($filter, $filterArguments); } } return $output; }; }
/** * If no custom callback is defined for a field, it will fall back to this * method to find a suitable callback. In the case of the Header helper, * we fall back to all field's just returning their label. * * @param FieldInterface $field * @return callable */ public function detectCallableForField(FieldInterface $field) { return function () use($field) { return $this->view->escapeHtml($field->getLabel()); }; }
/** * Try to detect a default callback for the provided field. THis helper * will only provide a default for database fields of common types. For * custom fields, you'll have to assign your own callback, if you want them * to be sortable. * * @param FieldInterface $field * @return false|callable */ public function detectCallableForField(FieldInterface $field) { $method = null; if (!$field instanceof DbField) { return false; } if ($field->isType('reference')) { $method = 'sortDbReference'; } elseif ($field->isType('date', 'timestamp')) { $method = 'sortDbDate'; } elseif ($field->isType('manytomany', 'clob', 'string', 'numeric', 'boolean')) { $method = 'sortDbDefault'; } if (!$method) { return false; } else { return function ($helper, Select $select, $direction) use($field, $method) { return $this->{$method}($field, $select, $direction); }; } }
/** * If no custom callback is defined for a field, it will fall back to this * method to find a suitable callback. In the case of the Header helper, * we fall back to all fields just returning their labels. * * @param FieldInterface $field * @return callable */ public function detectCallableForField(FieldInterface $field) { return function () use($field) { return $field->getLabel(); }; }
public function detectCallableForField(FieldInterface $field) { $type = null; if (!$field instanceof DbField) { return false; } if ($field->isType('manytomany')) { /* @var $field \Dewdrop\Db\ManyToMany\Field */ $filter = new ManyToManyFilter($field->getManyToManyRelationship()); } else { if ($field->isType('reference')) { $type = 'Reference'; } elseif ($field->isType('boolean')) { $type = 'Boolean'; } elseif ($field->isType('date', 'timestamp')) { $type = 'Date'; } elseif ($field->isType('integer', 'float')) { $type = 'Numeric'; } elseif ($field->isType('clob', 'text')) { $type = 'Text'; } else { return false; } if ($field instanceof EavField) { $tableName = $field->getName(); $fieldName = 'value'; } else { $tableName = $field->getTable()->getTableName(); $fieldName = $field->getName(); } $className = '\\Dewdrop\\Db\\Select\\Filter\\' . $type; $filter = new $className($tableName, $fieldName); } return function ($helper, $select, $conditionSetName, $queryVars) use($filter) { return $filter->apply($select, $conditionSetName, $queryVars); }; }
public function renderFieldContent(FieldInterface $field, InputFilter $inputFilter, Renderer $renderer, $position, $renderLabels = true) { $output = ''; $input = $inputFilter->has($field->getId()) ? $inputFilter->get($field->getId()) : null; $messages = $input ? $input->getMessages() : null; $output .= sprintf($this->renderFormGroupOpenTag(), $messages ? ' has-feedback has-error alert alert-danger' : ''); $controlOutput = $renderer->getControlRenderer()->render($field, $position); if ($renderLabels && $this->controlRequiresLabel($controlOutput)) { $output .= $this->renderLabel($field, $renderer, $input); } $output .= $controlOutput; if ($messages) { $output .= $this->renderMessages($messages); } if ($field->getNote()) { $output .= sprintf('<div class="help-block">%s</div>', $this->view->escapeHtml($field->getNote())); } $output .= '</div>'; return $output; }
/** * If no custom callback is defined for a field, it will fall back to this * method to find a suitable callback. In the case of the Content helper, * we only provide a fall back for DB-based fields. Custom fields will have * to define a callback in order to function properly. * * @param FieldInterface $field * @return mixed */ public function detectCallableForField(FieldInterface $field) { $method = null; if (!$field instanceof DbField) { return false; } if ($field->isType('boolean')) { $method = 'renderDbBoolean'; } elseif ($field->isType('reference')) { $method = 'renderDbReference'; } elseif ($field->isType('date')) { $method = 'renderDbDate'; } elseif ($field->isType('timestamp')) { $method = 'renderDbTimestamp'; } elseif ($field->isType('manytomany', 'clob', 'string', 'numeric')) { $method = 'renderDbText'; } if (!$method) { return false; } else { return function ($helper, array $rowData) use($field, $method) { return $this->{$method}($field, $rowData); }; } }
/** * Get the validation messages associated with a specific field. * * @param FieldInterface $field * @return array */ public function getMessages(FieldInterface $field) { return $this->getInputFilter()->get($field->getId())->getMessages(); }
/** * Get the callback that will be used for the given FieldInterface object. * * @param FieldInterface $field * @throws \Dewdrop\Fields\Exception\HelperCallableNotAvailableForField * @return callable */ public function getFieldAssignment(FieldInterface $field) { if (!$this->hasValidName()) { return false; } $id = $field->getId(); if (!array_key_exists($id, $this->assignments)) { if (!$field->hasHelperCallback($this->name)) { $callback = $this->detectCallableForField($field); } else { $callback = $field->getHelperCallback($this->name); } if (!is_callable($callback)) { throw new HelperCallableNotAvailableForField("Field {$id} does not have a callable assigned and one could not be detected."); } $this->assignments[$id] = $this->wrapCallable($callback); } return $this->assignments[$id]; }
/** * Add the supplied field to this collection after another specified field already in the collection. Can be a * FieldInterface object or a string, in which case a new custom field will be added with the supplied string as its * ID. * * The newly added FieldInterface object is returned from this method so that it can be further customized * immediately, using method chaining. Once you've completed calling methods on the FieldInterface object itself, * you can call any \Dewdrop\Fields methods to return execution to that context. * * @param FieldInterface|string $field * @param FieldInterface|string $after * @param string $modelName * @throws Exception * @return FieldInterface */ public function insertAfter($field, $after, $modelName = null) { if ($after instanceof FieldInterface) { $afterId = $after->getId(); } else { $afterId = (string) $after; } if (!$this->has($afterId, $afterPosition)) { throw new Exception("Field with ID \"{$afterId}\" does not exist in this collection"); } $field = $this->prepareFieldForAdding($field, $modelName); array_splice($this->fields, $afterPosition + 1, 0, [$field]); return $field; }
/** * A shortcut/utility method that sets up an EditControl.Control callback * to use OptionInputDecorator. * * @param FieldInterface $field * @param CrudInterface $controlComponent * @param CrudInterface $optionComponent * @param callable $originalHtmlCallback */ public static function assignEditControlCallback(FieldInterface $field, CrudInterface $controlComponent, CrudInterface $optionComponent, callable $originalHtmlCallback = null) { $field->assignHelperCallback('EditControl.Control', OptionInputDecorator::createEditControlCallback($field, $controlComponent, $optionComponent, $originalHtmlCallback)); }