mirror of
https://github.com/silverstripe/silverstripe-framework
synced 2024-10-22 14:05:37 +02:00
ENHANCEMENT Adding List() in the GenericTemplateGlobalProvider so
templates can use <% cached List(Member).max(LastEdited) %> instead of deprecated "Aggregate" syntax.
This commit is contained in:
parent
d7a8fa9266
commit
792c89e103
@ -193,6 +193,17 @@ The abstract `RelationList` class and its implementations `ManyManyList` and `Ha
|
||||
are replacing the `ComponentSet` API, which is only relevant if you have instanciated these manually.
|
||||
Relations are retrieved through the same way (e.g. `$myMember->Groups()`).
|
||||
|
||||
### Aggregate changes for partial caching in templates ###
|
||||
|
||||
`DataObject::Aggregate()` and `DataObject::RelationshipAggregate()` are now deprecated. To replace your deprecated aggregate calls
|
||||
in PHP code, you should query with something like `Member::get()->max('LastEdited')`, that is, calling the aggregate on the `DataList` directly.
|
||||
The same concept applies for replacing `RelationshipAggregate()`, just call the aggregate method on the relationship instead,
|
||||
so something like `Member::get()->Groups()->max('LastEdited')`.
|
||||
|
||||
For partial caching in templates, the syntax `<% cached Aggregate(Page).Max(LastEdited) %>` has been deprecated. The new syntax is similar,
|
||||
except you use `List()` instead of `Aggregate()`, and the aggregate call `Max()` is now lowercase, as in `max()`.
|
||||
An example of the new syntax is `<% cached List(Page).max(LastEdited) %>`. Check `DataList` class for more aggregate methods to use.
|
||||
|
||||
### `SQLQuery` changes ###
|
||||
|
||||
`SQLQuery` has been changed so direct access to internal properties `$from`, `$select`, `$orderby` is
|
||||
|
@ -61,21 +61,21 @@ For example, if we have a menu, we want that menu to update whenever _any_ page
|
||||
otherwise. By using aggregates, that's easy
|
||||
|
||||
:::ss
|
||||
<% cached 'navigation', Aggregate(Page).Max(LastEdited) %>
|
||||
<% cached 'navigation', List(Page).max(LastEdited) %>
|
||||
|
||||
|
||||
If we have a block that shows a list of categories, we can make sure the cache updates every time a category is added or
|
||||
edited
|
||||
|
||||
:::ss
|
||||
<% cached 'categorylist', Aggregate(Category).Max(LastEdited) %>
|
||||
<% cached 'categorylist', List(Category).max(LastEdited) %>
|
||||
|
||||
|
||||
We can also calculate aggregates on relationships. A block that shows the current member's favourites needs to update
|
||||
whenever the relationship Member::$has_many = array('Favourites' => Favourite') changes.
|
||||
|
||||
:::ss
|
||||
<% cached 'favourites', CurrentMember.ID, CurrentMember.RelationshipAggregate(Favourites).Max(LastEdited) %>
|
||||
<% cached 'favourites', CurrentMember.ID, CurrentMember.Favourites.max(LastEdited) %>
|
||||
|
||||
|
||||
## Cache key calculated in controller
|
||||
@ -89,7 +89,7 @@ logic into the controller
|
||||
return implode('_', array(
|
||||
'favourites',
|
||||
$member->ID,
|
||||
$member->RelationshipAggregate('Favourites')->Max('LastEdited')
|
||||
$member->Favourites()->max('LastEdited')
|
||||
));
|
||||
}
|
||||
|
||||
@ -230,10 +230,10 @@ Can be re-written as:
|
||||
:::ss
|
||||
<% cached LastEdited %>
|
||||
|
||||
<% cached RelationshipAggregate(Children).Max(LastEdited) %>
|
||||
<% cached Children.max(LastEdited) %>
|
||||
<% control Children %>
|
||||
$Name
|
||||
<% end_control %>
|
||||
<% end_cached %>
|
||||
|
||||
<% end_cached %>
|
||||
<% end_cached %>
|
||||
|
@ -2659,16 +2659,18 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity
|
||||
* @deprecated 3.0 Use DataList::create and DataList to do your querying
|
||||
*/
|
||||
public function Aggregate($class = null) {
|
||||
Deprecation::notice('3.0', 'Use DataList::create and DataList to do your querying instead.');
|
||||
Deprecation::notice('3.0', 'Call aggregate methods on a DataList directly instead. In templates ' .
|
||||
'an example of the new syntax is <% cached List(Member).max(LastEdited) %> instead (check partial-caching.md documentation ' .
|
||||
'for more details.)');
|
||||
|
||||
if($class) {
|
||||
if($class) {
|
||||
$list = new DataList($class);
|
||||
$list->setDataModel(DataModel::inst());
|
||||
} else if(isset($this)) {
|
||||
$list = new DataList(get_class($this));
|
||||
$list->setDataModel($this->model);
|
||||
}
|
||||
else throw new InvalidArgumentException("DataObject::aggregate() must be called as an instance method or passed a classname");
|
||||
else throw new InvalidArgumentException("DataObject::aggregate() must be called as an instance method or passed a classname");
|
||||
return $list;
|
||||
}
|
||||
|
||||
@ -2676,9 +2678,9 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity
|
||||
* @deprecated 3.0 Use DataList::create and DataList to do your querying
|
||||
*/
|
||||
public function RelationshipAggregate($relationship) {
|
||||
Deprecation::notice('3.0', 'Use DataList::create and DataList to do your querying instead.');
|
||||
Deprecation::notice('3.0', 'Call aggregate methods on a relationship directly instead.');
|
||||
|
||||
return $this->$relationship();
|
||||
return $this->$relationship();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -315,7 +315,7 @@ class DataQuery {
|
||||
* Note that this will issue a separate SELECT COUNT() query.
|
||||
*/
|
||||
function count() {
|
||||
$baseClass = ClassInfo::baseDataClass($this->dataClass);
|
||||
$baseClass = ClassInfo::baseDataClass($this->dataClass);
|
||||
return $this->getFinalisedQuery()->count("DISTINCT \"$baseClass\".\"ID\"");
|
||||
}
|
||||
|
||||
@ -324,8 +324,8 @@ class DataQuery {
|
||||
*
|
||||
* @param String $field Unquoted database column name (will be escaped automatically)
|
||||
*/
|
||||
function Max($field) {
|
||||
return $this->getFinalisedQuery()->aggregate(sprintf('MAX("%s")', Convert::raw2sql($field)))->execute()->value();
|
||||
function max($field) {
|
||||
return $this->getFinalisedQuery()->aggregate(sprintf('MAX("%s")', Convert::raw2sql($field)))->execute()->value();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -333,8 +333,8 @@ class DataQuery {
|
||||
*
|
||||
* @param String $field Unquoted database column name (will be escaped automatically)
|
||||
*/
|
||||
function Min($field) {
|
||||
return $this->getFinalisedQuery()->aggregate(sprintf('MIN("%s")', Convert::raw2sql($field)))->execute()->value();
|
||||
function min($field) {
|
||||
return $this->getFinalisedQuery()->aggregate(sprintf('MIN("%s")', Convert::raw2sql($field)))->execute()->value();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -342,8 +342,8 @@ class DataQuery {
|
||||
*
|
||||
* @param String $field Unquoted database column name (will be escaped automatically)
|
||||
*/
|
||||
function Avg($field) {
|
||||
return $this->getFinalisedQuery()->aggregate(sprintf('AVG("%s")', Convert::raw2sql($field)))->execute()->value();
|
||||
function avg($field) {
|
||||
return $this->getFinalisedQuery()->aggregate(sprintf('AVG("%s")', Convert::raw2sql($field)))->execute()->value();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -351,8 +351,8 @@ class DataQuery {
|
||||
*
|
||||
* @param String $field Unquoted database column name (will be escaped automatically)
|
||||
*/
|
||||
function Sum($field) {
|
||||
return $this->getFinalisedQuery()->aggregate(sprintf('SUM("%s")', Convert::raw2sql($field)))->execute()->value();
|
||||
function sum($field) {
|
||||
return $this->getFinalisedQuery()->aggregate(sprintf('SUM("%s")', Convert::raw2sql($field)))->execute()->value();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1,7 +1,13 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* A hierarchy of data types, to
|
||||
* A hierarchy of data types, to...
|
||||
*
|
||||
* @deprecated. This is testing`
|
||||
* {@link DataObject::Aggregate()} and {@link DataObject::RelationshipAggregate()}
|
||||
* which are deprecated. Aggregates are handled directly by DataList instead.
|
||||
* This test should be removed or merged into DataListTest once those functions are
|
||||
* removed from DataObject.
|
||||
*/
|
||||
class AggregateTest_Foo extends DataObject implements TestOnly {
|
||||
static $db = array(
|
||||
|
@ -54,7 +54,7 @@ class SSViewerTest extends SapphireTest {
|
||||
|
||||
$this->assertFalse((bool)trim($template), "Should be no content in this return.");
|
||||
}
|
||||
|
||||
|
||||
function testComments() {
|
||||
$output = $this->render(<<<SS
|
||||
This is my template<%-- this is a comment --%>This is some content<%-- this is another comment --%>This is the final content
|
||||
|
@ -3,7 +3,8 @@ class GenericTemplateGlobalProvider implements TemplateGlobalProvider {
|
||||
|
||||
public static function get_template_global_variables() {
|
||||
return array(
|
||||
'ModulePath'
|
||||
'ModulePath',
|
||||
'List' => 'getDataList'
|
||||
);
|
||||
}
|
||||
|
||||
@ -30,5 +31,24 @@ class GenericTemplateGlobalProvider implements TemplateGlobalProvider {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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>
|
||||
*
|
||||
* @return DataList
|
||||
*/
|
||||
public static function getDataList($className) {
|
||||
$list = new DataList($className);
|
||||
$list->setDataModel(DataModel::inst());
|
||||
return $list;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
@ -51,14 +51,13 @@ class SSViewer_Scope {
|
||||
list($this->item, $this->itemIterator, $this->itemIteratorTotal, $this->popIndex, $this->upIndex, $this->currentIndex) = $this->itemStack[$this->localIndex];
|
||||
array_splice($this->itemStack, $this->localIndex+1);
|
||||
}
|
||||
|
||||
|
||||
function getObj($name, $arguments = null, $forceReturnedObject = true, $cache = false, $cacheName = null) {
|
||||
$on = $this->itemIterator ? $this->itemIterator->current() : $this->item;
|
||||
return $on->obj($name, $arguments, $forceReturnedObject, $cache, $cacheName);
|
||||
}
|
||||
|
||||
function obj($name, $arguments = null, $forceReturnedObject = true, $cache = false, $cacheName = null){
|
||||
|
||||
|
||||
function obj($name, $arguments = null, $forceReturnedObject = true, $cache = false, $cacheName = null) {
|
||||
switch ($name) {
|
||||
case 'Up':
|
||||
if ($this->upIndex === null) user_error('Up called when we\'re already at the top of the scope', E_USER_ERROR);
|
||||
@ -72,13 +71,12 @@ class SSViewer_Scope {
|
||||
|
||||
default:
|
||||
$this->item = $this->getObj($name, $arguments, $forceReturnedObject, $cache, $cacheName);
|
||||
|
||||
$this->itemIterator = null;
|
||||
$this->upIndex = $this->currentIndex ? $this->currentIndex : count($this->itemStack)-1;
|
||||
$this->currentIndex = count($this->itemStack);
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
$this->itemStack[] = array($this->item, $this->itemIterator, $this->itemIteratorTotal, null, $this->upIndex, $this->currentIndex);
|
||||
return $this;
|
||||
}
|
||||
@ -380,7 +378,6 @@ class SSViewer_DataPresenter extends SSViewer_Scope {
|
||||
// Then for iterator-specific overrides
|
||||
else if (array_key_exists($property, self::$iteratorProperties)) {
|
||||
$source = self::$iteratorProperties[$property];
|
||||
|
||||
if ($this->itemIterator) {
|
||||
// Set the current iterator position and total (the object instance is the first item in the callable array)
|
||||
$source['implementer']->iteratorProperties($this->itemIterator->key(), $this->itemIteratorTotal);
|
||||
@ -423,6 +420,7 @@ class SSViewer_DataPresenter extends SSViewer_Scope {
|
||||
|
||||
return $res;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function getObj($name, $arguments = null, $forceReturnedObject = true, $cache = false, $cacheName = null) {
|
||||
|
Loading…
Reference in New Issue
Block a user