public function testNoSpecificColumnNamesSubclassDataObjectQuery()
 {
     // This queries all columns from base table and subtable
     $playerList = new DataList('DataObjectTest_SubTeam');
     // Should be a left join.
     $this->assertEquals(1, preg_match($this->normaliseSQL('/SELECT DISTINCT .* LEFT JOIN .* /'), $this->normaliseSQL($playerList->sql($parameters))));
 }
 public function scaffoldFormField($title = null, $params = null)
 {
     if (empty($this->object)) {
         return null;
     }
     $relationName = substr($this->name, 0, -2);
     $hasOneClass = DataObject::getSchema()->hasOneComponent(get_class($this->object), $relationName);
     if (empty($hasOneClass)) {
         return null;
     }
     $hasOneSingleton = singleton($hasOneClass);
     if ($hasOneSingleton instanceof File) {
         $field = new UploadField($relationName, $title);
         if ($hasOneSingleton instanceof Image) {
             $field->setAllowedFileCategories('image/supported');
         }
         return $field;
     }
     // Build selector / numeric field
     $titleField = $hasOneSingleton->hasField('Title') ? "Title" : "Name";
     $list = DataList::create($hasOneClass);
     // Don't scaffold a dropdown for large tables, as making the list concrete
     // might exceed the available PHP memory in creating too many DataObject instances
     if ($list->count() < 100) {
         $field = new DropdownField($this->name, $title, $list->map('ID', $titleField));
         $field->setEmptyString(' ');
     } else {
         $field = new NumericField($this->name, $title);
     }
     return $field;
 }
 public function getQueryParams()
 {
     $params = parent::getQueryParams();
     // Remove `Foreign.` query parameters for created objects,
     // as this would interfere with relations on those objects.
     foreach (array_keys($params) as $key) {
         if (stripos($key, 'Foreign.') === 0) {
             unset($params[$key]);
         }
     }
     return $params;
 }
 /**
  * Return all objects matching the filter
  * sub-classes are automatically selected and included
  *
  * @param string $callerClass The class of objects to be returned
  * @param string|array $filter A filter to be inserted into the WHERE clause.
  * Supports parameterised queries. See SQLSelect::addWhere() for syntax examples.
  * @param string|array $sort A sort expression to be inserted into the ORDER
  * BY clause.  If omitted, self::$default_sort will be used.
  * @param string $join Deprecated 3.0 Join clause. Use leftJoin($table, $joinClause) instead.
  * @param string|array $limit A limit expression to be inserted into the LIMIT clause.
  * @param string $containerClass The container class to return the results in.
  *
  * @todo $containerClass is Ignored, why?
  *
  * @return DataList The objects matching the filter, in the class specified by $containerClass
  */
 public static function get($callerClass = null, $filter = "", $sort = "", $join = "", $limit = null, $containerClass = DataList::class)
 {
     if ($callerClass == null) {
         $callerClass = get_called_class();
         if ($callerClass == self::class) {
             throw new \InvalidArgumentException('Call <classname>::get() instead of DataObject::get()');
         }
         if ($filter || $sort || $join || $limit || $containerClass != DataList::class) {
             throw new \InvalidArgumentException('If calling <classname>::get() then you shouldn\'t pass any other' . ' arguments');
         }
         $result = DataList::create(get_called_class());
         $result->setDataModel(DataModel::inst());
         return $result;
     }
     if ($join) {
         throw new \InvalidArgumentException('The $join argument has been removed. Use leftJoin($table, $joinClause) instead.');
     }
     $result = DataList::create($callerClass)->where($filter)->sort($sort);
     if ($limit && strpos($limit, ',') !== false) {
         $limitArguments = explode(',', $limit);
         $result = $result->limit($limitArguments[1], $limitArguments[0]);
     } elseif ($limit) {
         $result = $result->limit($limit);
     }
     $result->setDataModel(DataModel::inst());
     return $result;
 }
 public function testDataClassCaseInsensitive()
 {
     $list = DataList::create('dataobjecttest_teamcomment');
     $this->assertTrue($list->exists());
 }
 /**
  * The core search engine, used by this class and its subclasses to do fun stuff.
  * Searches both SiteTree and File.
  *
  * @param array $classesToSearch
  * @param string $keywords Keywords as a string.
  * @param int $start
  * @param int $pageLength
  * @param string $sortBy
  * @param string $extraFilter
  * @param bool $booleanSearch
  * @param string $alternativeFileFilter
  * @param bool $invertedMatch
  * @return PaginatedList
  * @throws Exception
  */
 public function searchEngine($classesToSearch, $keywords, $start, $pageLength, $sortBy = "Relevance DESC", $extraFilter = "", $booleanSearch = false, $alternativeFileFilter = "", $invertedMatch = false)
 {
     $pageClass = 'SilverStripe\\CMS\\Model\\SiteTree';
     $fileClass = 'File';
     $pageTable = DataObject::getSchema()->tableName($pageClass);
     $fileTable = DataObject::getSchema()->tableName($fileClass);
     if (!class_exists($pageClass)) {
         throw new Exception('MySQLDatabase->searchEngine() requires "SiteTree" class');
     }
     if (!class_exists($fileClass)) {
         throw new Exception('MySQLDatabase->searchEngine() requires "File" class');
     }
     $keywords = $this->escapeString($keywords);
     $htmlEntityKeywords = htmlentities($keywords, ENT_NOQUOTES, 'UTF-8');
     $extraFilters = array($pageClass => '', $fileClass => '');
     if ($booleanSearch) {
         $boolean = "IN BOOLEAN MODE";
     }
     if ($extraFilter) {
         $extraFilters[$pageClass] = " AND {$extraFilter}";
         if ($alternativeFileFilter) {
             $extraFilters[$fileClass] = " AND {$alternativeFileFilter}";
         } else {
             $extraFilters[$fileClass] = $extraFilters[$pageClass];
         }
     }
     // Always ensure that only pages with ShowInSearch = 1 can be searched
     $extraFilters[$pageClass] .= " AND ShowInSearch <> 0";
     // File.ShowInSearch was added later, keep the database driver backwards compatible
     // by checking for its existence first
     $fields = $this->getSchemaManager()->fieldList($fileTable);
     if (array_key_exists('ShowInSearch', $fields)) {
         $extraFilters[$fileClass] .= " AND ShowInSearch <> 0";
     }
     $limit = $start . ", " . (int) $pageLength;
     $notMatch = $invertedMatch ? "NOT " : "";
     if ($keywords) {
         $match[$pageClass] = "\n\t\t\t\tMATCH (Title, MenuTitle, Content, MetaDescription) AGAINST ('{$keywords}' {$boolean})\n\t\t\t\t+ MATCH (Title, MenuTitle, Content, MetaDescription) AGAINST ('{$htmlEntityKeywords}' {$boolean})\n\t\t\t";
         $fileClassSQL = Convert::raw2sql($fileClass);
         $match[$fileClass] = "MATCH (Name, Title) AGAINST ('{$keywords}' {$boolean}) AND ClassName = '{$fileClassSQL}'";
         // We make the relevance search by converting a boolean mode search into a normal one
         $relevanceKeywords = str_replace(array('*', '+', '-'), '', $keywords);
         $htmlEntityRelevanceKeywords = str_replace(array('*', '+', '-'), '', $htmlEntityKeywords);
         $relevance[$pageClass] = "MATCH (Title, MenuTitle, Content, MetaDescription) " . "AGAINST ('{$relevanceKeywords}') " . "+ MATCH (Title, MenuTitle, Content, MetaDescription) AGAINST ('{$htmlEntityRelevanceKeywords}')";
         $relevance[$fileClass] = "MATCH (Name, Title) AGAINST ('{$relevanceKeywords}')";
     } else {
         $relevance[$pageClass] = $relevance[$fileClass] = 1;
         $match[$pageClass] = $match[$fileClass] = "1 = 1";
     }
     // Generate initial DataLists and base table names
     $lists = array();
     $baseClasses = array($pageClass => '', $fileClass => '');
     foreach ($classesToSearch as $class) {
         $lists[$class] = DataList::create($class)->where($notMatch . $match[$class] . $extraFilters[$class], "");
         $baseClasses[$class] = '"' . $class . '"';
     }
     $charset = Config::inst()->get('SilverStripe\\ORM\\Connect\\MySQLDatabase', 'charset');
     // Make column selection lists
     $select = array($pageClass => array("ClassName", "{$pageTable}.\"ID\"", "ParentID", "Title", "MenuTitle", "URLSegment", "Content", "LastEdited", "Created", "Name" => "_{$charset}''", "Relevance" => $relevance[$pageClass], "CanViewType"), $fileClass => array("ClassName", "{$fileTable}.\"ID\"", "ParentID", "Title", "MenuTitle" => "_{$charset}''", "URLSegment" => "_{$charset}''", "Content" => "_{$charset}''", "LastEdited", "Created", "Name", "Relevance" => $relevance[$fileClass], "CanViewType" => "NULL"));
     // Process and combine queries
     $querySQLs = array();
     $queryParameters = array();
     $totalCount = 0;
     foreach ($lists as $class => $list) {
         $table = DataObject::getSchema()->tableName($class);
         /** @var SQLSelect $query */
         $query = $list->dataQuery()->query();
         // There's no need to do all that joining
         $query->setFrom($table);
         $query->setSelect($select[$class]);
         $query->setOrderBy(array());
         $querySQLs[] = $query->sql($parameters);
         $queryParameters = array_merge($queryParameters, $parameters);
         $totalCount += $query->unlimitedRowCount();
     }
     $fullQuery = implode(" UNION ", $querySQLs) . " ORDER BY {$sortBy} LIMIT {$limit}";
     // Get records
     $records = $this->preparedQuery($fullQuery, $queryParameters);
     $objects = array();
     foreach ($records as $record) {
         $objects[] = new $record['ClassName']($record);
     }
     $list = new PaginatedList(new ArrayList($objects));
     $list->setPageStart($start);
     $list->setPageLength($pageLength);
     $list->setTotalItems($totalCount);
     // The list has already been limited by the query above
     $list->setLimitItems(false);
     return $list;
 }
 /**
  * Returns a json array of a search results that can be used by for example Jquery.ui.autosuggestion
  *
  * @param GridField $gridField
  * @param HTTPRequest $request
  * @return string
  */
 public function doSearch($gridField, $request)
 {
     $dataClass = $gridField->getModelClass();
     $allList = $this->searchList ? $this->searchList : DataList::create($dataClass);
     $searchFields = $this->getSearchFields() ? $this->getSearchFields() : $this->scaffoldSearchFields($dataClass);
     if (!$searchFields) {
         throw new LogicException(sprintf('GridFieldAddExistingAutocompleter: No searchable fields could be found for class "%s"', $dataClass));
     }
     $params = array();
     foreach ($searchFields as $searchField) {
         $name = strpos($searchField, ':') !== FALSE ? $searchField : "{$searchField}:StartsWith";
         $params[$name] = $request->getVar('gridfield_relationsearch');
     }
     $results = $allList->subtract($gridField->getList())->filterAny($params)->sort(strtok($searchFields[0], ':'), 'ASC')->limit($this->getResultsLimit());
     $json = array();
     Config::nest();
     SSViewer::config()->update('source_file_comments', false);
     $viewer = SSViewer::fromString($this->resultsFormat);
     foreach ($results as $result) {
         $title = html_entity_decode($viewer->process($result));
         $json[] = array('label' => $title, 'value' => $title, 'id' => $result->ID);
     }
     Config::unnest();
     return Convert::array2json($json);
 }
 /**
  * Returns a SQL object representing the search context for the given
  * list of query parameters.
  *
  * @param array $searchParams Map of search criteria, mostly taked from $_REQUEST.
  *  If a filter is applied to a relationship in dot notation,
  *  the parameter name should have the dots replaced with double underscores,
  *  for example "Comments__Name" instead of the filter name "Comments.Name".
  * @param array|bool|string $sort Database column to sort on.
  *  Falls back to {@link DataObject::$default_sort} if not provided.
  * @param array|bool|string $limit
  * @param DataList $existingQuery
  * @return DataList
  * @throws Exception
  */
 public function getQuery($searchParams, $sort = false, $limit = false, $existingQuery = null)
 {
     /** DataList $query */
     if ($existingQuery) {
         if (!$existingQuery instanceof DataList) {
             throw new InvalidArgumentException("existingQuery must be DataList");
         }
         if ($existingQuery->dataClass() != $this->modelClass) {
             throw new InvalidArgumentException("existingQuery's dataClass is " . $existingQuery->dataClass() . ", {$this->modelClass} expected.");
         }
         $query = $existingQuery;
     } else {
         $query = DataList::create($this->modelClass);
     }
     if (is_array($limit)) {
         $query = $query->limit(isset($limit['limit']) ? $limit['limit'] : null, isset($limit['start']) ? $limit['start'] : null);
     } else {
         $query = $query->limit($limit);
     }
     $query = $query->sort($sort);
     // hack to work with $searchParems when it's an Object
     if ($searchParams instanceof HTTPRequest) {
         $searchParamArray = $searchParams->getVars();
     } else {
         $searchParamArray = $searchParams;
     }
     foreach ($searchParamArray as $key => $value) {
         $key = str_replace('__', '.', $key);
         if ($filter = $this->getFilter($key)) {
             $filter->setModel($this->modelClass);
             $filter->setValue($value);
             if (!$filter->isEmpty()) {
                 $query = $query->alterDataQuery(array($filter, 'apply'));
             }
         }
     }
     if ($this->connective != "AND") {
         throw new Exception("SearchContext connective '{$this->connective}' not supported after ORM-rewrite.");
     }
     return $query;
 }
 /**
  * This allows templates to create a new `DataList` from a known
  * DataObject class name, and call methods such as aggregates.
  *
  * The common use case is for partial caching:
  * <code>
  *    <% cached List(Member).max(LastEdited) %>
  *        loop members here
  *    <% end_cached %>
  * </code>
  *
  * @param string $className
  * @return DataList
  */
 public static function getDataList($className)
 {
     $list = new DataList($className);
     $list->setDataModel(DataModel::inst());
     return $list;
 }
 /**
  * @param int $folderID The ID of the folder to display.
  * @return FormField
  */
 protected function getListField($folderID)
 {
     // Generate the folder selection field.
     /** @skipUpgrade */
     $folderField = new TreeDropdownField('ParentID', _t('HTMLEditorField.FOLDER', 'Folder'), 'SilverStripe\\Assets\\Folder');
     $folderField->setValue($folderID);
     // Generate the file list field.
     $config = GridFieldConfig::create();
     $config->addComponent(new GridFieldSortableHeader());
     $config->addComponent(new GridFieldFilterHeader());
     $config->addComponent($colsComponent = new GridFieldDataColumns());
     $colsComponent->setDisplayFields(array('StripThumbnail' => '', 'Title' => File::singleton()->fieldLabel('Title'), 'Created' => File::singleton()->fieldLabel('Created'), 'Size' => File::singleton()->fieldLabel('Size')));
     $colsComponent->setFieldCasting(array('Created' => 'DBDatetime->Nice'));
     // Set configurable pagination for file list field
     $pageSize = Config::inst()->get(get_class($this), 'page_size');
     $config->addComponent(new GridFieldPaginator($pageSize));
     // If relation is to be autoset, we need to make sure we only list compatible objects.
     $baseClass = $this->parent->getRelationAutosetClass();
     // Create the data source for the list of files within the current directory.
     $files = DataList::create($baseClass)->exclude('ClassName', 'SilverStripe\\Assets\\Folder');
     if ($folderID) {
         $files = $files->filter('ParentID', $folderID);
     }
     $fileField = new GridField('Files', false, $files, $config);
     $fileField->setAttribute('data-selectable', true);
     if ($this->parent->getAllowedMaxFileNumber() !== 1) {
         $fileField->setAttribute('data-multiselect', true);
     }
     $selectComposite = new CompositeField($folderField, $fileField);
     return $selectComposite;
 }
Пример #11
0
 /**
  * Return a list of all versions for a given id.
  *
  * @param string $class
  * @param int $id
  *
  * @return DataList
  */
 public static function get_all_versions($class, $id)
 {
     $list = DataList::create($class)->filter('ID', $id)->setDataQueryParam('Versioned.mode', 'all_versions');
     return $list;
 }
Пример #12
0
 /**
  * This method returns a copy of this list that does not contain any DataObjects that exists in $list
  *
  * The $list passed needs to contain the same dataclass as $this
  *
  * @param DataList $list
  * @return DataList
  * @throws BadMethodCallException
  */
 public function subtract(DataList $list)
 {
     if ($this->dataClass() != $list->dataClass()) {
         throw new InvalidArgumentException('The list passed must have the same dataclass as this class');
     }
     return $this->alterDataQuery(function (DataQuery $query) use($list) {
         $query->subtract($list->dataQuery());
     });
 }