API Better behaviour for virtualised controller

This commit is contained in:
Damian Mooyman 2016-07-25 10:05:03 +12:00
parent 009246dc62
commit 70179c8b87
No known key found for this signature in database
GPG Key ID: 78B823A10DE27D1A
3 changed files with 83 additions and 24 deletions

View File

@ -2,6 +2,7 @@
use SilverStripe\ORM\DataObject; use SilverStripe\ORM\DataObject;
use SilverStripe\ORM\Versioning\Versioned; use SilverStripe\ORM\Versioning\Versioned;
use SilverStripe\Security\Member;
/** /**
* Virtual Page creates an instance of a page, with the same fields that the original page had, but readonly. * Virtual Page creates an instance of a page, with the same fields that the original page had, but readonly.
@ -9,6 +10,7 @@ use SilverStripe\ORM\Versioning\Versioned;
* Note: This Only duplicates $db fields and not the $has_one etc.. * Note: This Only duplicates $db fields and not the $has_one etc..
* *
* @method SiteTree CopyContentFrom() * @method SiteTree CopyContentFrom()
* @property int $CopyContentFromID
* *
* @package cms * @package cms
*/ */
@ -317,7 +319,7 @@ class VirtualPage extends Page {
unset($this->components['CopyContentFrom']); unset($this->components['CopyContentFrom']);
// Update ImageTracking // Update ImageTracking
$this->ImageTracking()->setByIdList($this->CopyContentFrom()->ImageTracking()->column('ID')); $this->ImageTracking()->setByIDList($this->CopyContentFrom()->ImageTracking()->column('ID'));
} }
/** /**
@ -338,11 +340,14 @@ class VirtualPage extends Page {
public function __get($field) { public function __get($field) {
if(parent::hasMethod($funcName = "get$field")) { if(parent::hasMethod($funcName = "get$field")) {
return $this->$funcName(); return $this->$funcName();
} else if(parent::hasField($field) || ($field === 'ID' && !$this->exists())) { }
if(parent::hasField($field) || ($field === 'ID' && !$this->exists())) {
return $this->getField($field); return $this->getField($field);
} elseif(($copy = $this->CopyContentFrom()) && $copy->exists()) { }
if(($copy = $this->CopyContentFrom()) && $copy->exists()) {
return $copy->$field; return $copy->$field;
} }
return null;
} }
public function getField($field) { public function getField($field) {
@ -386,7 +391,7 @@ class VirtualPage extends Page {
if(parent::hasMethod($method)) { if(parent::hasMethod($method)) {
return parent::__call($method, $args); return parent::__call($method, $args);
} else { } else {
return call_user_func_array(array($this->copyContentFrom(), $method), $args); return call_user_func_array(array($this->CopyContentFrom(), $method), $args);
} }
} }
@ -447,11 +452,38 @@ class VirtualPage_Controller extends Page_Controller {
'loadcontentall' => 'ADMIN', 'loadcontentall' => 'ADMIN',
); );
/**
* Backup of virtualised controller
*
* @var ContentController
*/
protected $virtualController = null;
/**
* Get virtual controller
*
* @return ContentController
*/
protected function getVirtualisedController() {
if($this->virtualController) {
return $this->virtualController;
}
// Validate virtualised model
/** @var VirtualPage $page */
$page = $this->data();
$virtualisedPage = $page->CopyContentFrom();
if (!$virtualisedPage || !$virtualisedPage->exists()) {
return null;
}
// Create controller using standard mechanism
$this->virtualController = ModelAsController::controller_for($virtualisedPage);
return $this->virtualController;
}
public function getViewer($action) { public function getViewer($action) {
$originalClass = get_class($this->CopyContentFrom()); $controller = $this->getVirtualisedController() ?: $this;
if ($originalClass == 'SiteTree') $name = 'Page_Controller';
else $name = $originalClass."_Controller";
$controller = new $name();
return $controller->getViewer($action); return $controller->getViewer($action);
} }
@ -473,15 +505,13 @@ class VirtualPage_Controller extends Page_Controller {
* @return bool * @return bool
*/ */
public function hasMethod($method) { public function hasMethod($method) {
$haveIt = parent::hasMethod($method); if(parent::hasMethod($method)) {
if (!$haveIt) { return true;
$originalClass = get_class($this->CopyContentFrom()); };
if ($originalClass == 'SiteTree') $name = 'ContentController';
else $name = $originalClass."_Controller"; // Fallback
$controller = new $name($this->dataRecord->copyContentFrom()); $controller = $this->getVirtualisedController();
$haveIt = $controller->hasMethod($method); return $controller && $controller->hasMethod($method);
}
return $haveIt;
} }
/** /**
@ -493,23 +523,24 @@ class VirtualPage_Controller extends Page_Controller {
* *
* @throws Exception Any error other than a 'no method' error. * @throws Exception Any error other than a 'no method' error.
*/ */
public function __call($method, $args) { public function __call($method, $args)
{
// Check if we can safely call this method before passing it back // Check if we can safely call this method before passing it back
// to custom methods. // to custom methods.
if($this->getExtraMethodConfig($method)) { if ($this->getExtraMethodConfig($method)) {
return parent::__call($method, $args); return parent::__call($method, $args);
} }
// Pass back to copied page // Pass back to copied page
$original = $this->copyContentFrom(); $controller = $this->getVirtualisedController();
$controller = ModelAsController::controller_for($original); if(!$controller) {
return null;
}
// Ensure request/response data is available on virtual controller // Ensure request/response data is available on virtual controller
$controller->setRequest($this->getRequest()); $controller->setRequest($this->getRequest());
$controller->response = $this->response; // @todo - replace with getter/setter in 3.3 $controller->setResponse($this->getResponse());
return call_user_func_array(array($controller, $method), $args); return call_user_func_array(array($controller, $method), $args);
} }
} }

View File

@ -601,6 +601,17 @@ class VirtualPageTest extends FunctionalTest {
$this->assertEquals(301, $response->getStatusCode()); $this->assertEquals(301, $response->getStatusCode());
$this->assertEquals('http://google.com', $response->getHeader('Location')); $this->assertEquals('http://google.com', $response->getHeader('Location'));
} }
public function testMethod() {
$virtualPage = $this->objFromFixture('VirtualPage', 'vp4');
$controller = ModelAsController::controller_for($virtualPage);
$this->assertInstanceOf('VirtualPage_Controller', $controller);
$this->assertTrue($controller->hasMethod('testMethod'));
$this->assertEquals('hello', $controller->testMethod());
$this->assertTrue($controller->hasMethod('modelMethod'));
$this->assertEquals('hi there', $controller->modelMethod());
}
} }
class VirtualPageTest_ClassA extends Page implements TestOnly { class VirtualPageTest_ClassA extends Page implements TestOnly {
@ -613,6 +624,16 @@ class VirtualPageTest_ClassA extends Page implements TestOnly {
); );
private static $allowed_children = array('VirtualPageTest_ClassB'); private static $allowed_children = array('VirtualPageTest_ClassB');
public function modelMethod() {
return 'hi there';
}
}
class VirtualPageTest_ClassA_Controller extends Page_Controller implements TestOnly {
public function testMethod() {
return 'hello';
}
} }
class VirtualPageTest_ClassB extends Page implements TestOnly { class VirtualPageTest_ClassB extends Page implements TestOnly {

View File

@ -45,6 +45,10 @@ Page:
CanEditType: OnlyTheseUsers CanEditType: OnlyTheseUsers
CanViewType: Inherit CanViewType: Inherit
EditorGroups: =>SilverStripe\Security\Group.bobgroup EditorGroups: =>SilverStripe\Security\Group.bobgroup
VirtualPageTest_ClassA:
pagea:
Title: 'Page A'
Content: '<p>Content</p>'
VirtualPage: VirtualPage:
vp1: vp1:
Title: vp1 Title: vp1
@ -61,3 +65,6 @@ VirtualPage:
CanViewType: OnlyTheseUsers CanViewType: OnlyTheseUsers
EditorGroups: =>SilverStripe\Security\Group.andrewgroup EditorGroups: =>SilverStripe\Security\Group.andrewgroup
ViewerGroups: =>SilverStripe\Security\Group.cindygroup ViewerGroups: =>SilverStripe\Security\Group.cindygroup
vp4:
CopyContentFrom: =>VirtualPageTest_ClassA.pagea
Title: 'vp4'