NOTE: The php class structures described below are an experimental technique and should (a) - be used with caution, and (b) - be very thoroughly tested.
Here is a solution for a block controller chicken and egg problem. You have a package with two block types in it, A and B. Block type B has a lot of shared functionality with block type A, so you would like the controller for block B to inherit from the controller for block A.
At first, it seems very simple. PHP provides class inheritance. You should be able to write:
blocks/my_block_a/controller.php
class MyBlockABlockController extends BlockController {
protected $btTable = "btMyBlockA";
// more block A class
}
and:
blocks/my_block_b/controller.php
Loader::block('my_block_a');
class MyBlockBBlockController extends MyBlockABlockController {
protected $btTable = "btMyBlockB";
// block B class inheriting from block A
}
The catch is loading the controller for block type A. Block controllers also need to be loaded when a package installs the blocks.
public function install() {
$pkg = parent::install();
BlockType::installBlockTypeFromPackage('my_block_a', $pkg);
BlockType::installBlockTypeFromPackage('my_block_b', $pkg);
}
However, as far as concrete5 is concerned, neither block actually exists in the database until after the package is installed, so at the time you try to install block B, block A does not yet exist. If try to do the above, block B will fail to install and the package installation will end with an error!
A simple solution can be to split them into two separate packages, where package B requires package A. However, it seems a little heavy handed to create a second package when all you wanted was a small variation in a block.
Another solution that has been used in the past is to make both A and B inherit from a library class that inherits from the base BlockController
class. It works, but it was never something I felt really comfortable with.
My alternate solution is a bit of class inheritance judo. The controller for block A is declared as normal.
blocks/my_block_a/controller.php
class MyBlockABlockController extends BlockController {
protected $btTable = "btMyBlockA";
// more block A class
}
Then, in the controller for block B, we create a conditional inheritance structure, testing to see if block A exists and inheriting from it when available (which is any time after the package installation is complete), or where block A is not available, simply inheriting from the base BlockController. To actually achieve this without repeating all of the code for block B's controller we need an intermediary class. the controller from B inherits from the intermediary, and the complete intermediary inherits from either A or the base BlockController.
blocks/my_block_b/controller.php
if(is_object(BlockType::getByHandle('my_block_a'))){
Loader::block('my_block_a');
abstract class MyBlockBIntermediary extends MyBlockABlockController {
public function inheritance_complete(){
return true;
}
}
}else{
abstract class MyBlockBIntermediary extends BlockController {
public function inheritance_complete(){
return false;
}
}
}
class MyBlockBBlockController extends MyBlockBIntermediary {
protected $btTable = "btMyBlockB";
// block B class inheriting from block A
}
With just a few additional lines at the top of controller B we can now override and extend just where needed within the controller for B, simply inheriting everything we need from block A.
During installation, block B misses out all the inheritance from block A and just inherits from the core BlockController. After installation, A is available and B inherits from it.
You can test this by calling the test inheritance_complete()
in the view for block B.
echo $controller->inheritance_complete();
You don't actually need to declare that function, its just there to demonstrate that everything is working as it should.
If you also want to re-use add/edit or view files from block a in block b, that is much simpler with a bit of path traversal in the inc()
calls.
blocks/my_block_b/view.php
$this->inc('../my_block_a/view.php');
REMEMBER: The php class structures described above are an experimental technique and should (a) - be used with caution, and (b) - be very thoroughly tested.