Sharing Code / Functionality Between Blocks

Permalink
This may seem like a simple question so I am hoping there is a simple answer.

I am relatively new to PHP and concrete5 and have just started developing a new package. My package contains two separate blocks but there is some duplicated functionality within the blocks controllers.

What I want to do is centralise this duplicated code into some kind of shared or common controller class so that if it needs to be changed I only have to change it in one place.

What would be the best / easiest way to achieve this.

Thanks

James

jheanly
 
KJLJon replied on at Permalink Reply
KJLJon
maybe make some of the common used functions into a 'helper' class?

Then in the two blocks you would just have to load the helper and call the functions.
jheanly replied on at Permalink Reply
jheanly
Hi KJLJon,

Thanks heaps for the prompt response. I will give it a shot using a custom built helper class.

Regards,

James
jordanlev replied on at Permalink Reply
jordanlev
I would probably just use a "library". In your package directory, add a directory called "libraries". Then put a code file in there. Then in your code, add this to include your library (so you'll be able to call any functions in it or load any classes in it):
Loader::library('library_file_name_without_php_at_the_end', 'your_package_handle');


So if your package handle is "cool_stuff" and the library file is called "cool_functionality.php", the code would be:
Loader::library('cool_functionality', 'cool_stuff');


The reason I'd recommend a library instead of a helper is because in C5, a helper implies one class that gets loaded and has static methods -- this can be somewhat restrictive (and confusing) if your common functionality doesn't fit into the mold of a class with static methods. Using a library as I've outlined above gives you flexibility to structure your common code any way you'd like.
JohntheFish replied on at Permalink Reply
JohntheFish
I have a similar issue, of 2 blocks in a package, one of which provides stripped down functionality of the other. For the reduced block edit/add/view, I am just doing 3 liners with $this->inc of files from the full block, which have some 'if this or that block' conditions to decide what they do.

For the controller, I tried to get the second block's controller to inherit from the first block's controller, but have not got it to work yet. So my current solution is just a cut and paste of the parts I need.

So any good tips on making the inheritance work between the 2 controllers would be appreciated.

(PS. I have used the library approach in the past, but this situation just cries out for some classic OO inheritance if only I can get it to work)
jordanlev replied on at Permalink Reply
jordanlev
I would still use the library approach (or $this->inc() , basically the same thing). But if you do want to use some OOP inheritance, you can make a class that extends BlockController in a library file, then your block controllers extend that. So in your site's "libraries" directory (or in your package "library" directory), add a file called better_block_controller.php, and in that file put this:
class BetterBlockController extends BlockController {
   //...
}

Then you block controllers would be this:
Loader::library('better_block_controller'); //or pass in package handle as 2nd arg if package library
class MyAwesomeThingBlockController extends BetterBlockController {
  //...
}
JohntheFish replied on at Permalink Reply
JohntheFish
Just to update an old thread, I found a way round the problem and posted a howto:

http://www.concrete5.org/documentation/how-tos/developers/inherit-b...
jordanlev replied on at Permalink Reply
jordanlev
Hey John,
Nice how-to -- that's great to know.

I'm curious to hear more about something you mention:

"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."

I personally find this solution to be the ideal one when I encounter this situation, and a lot more straightforward than anything else. What is it about that approach that you don't feel comfortable with?

Thanks,
Jordan
JohntheFish replied on at Permalink Reply
JohntheFish
Its taking block controller code out of the block controller. Methods like event handlers, view, add, edit, save, validate etc are very block-ish methods, so I like to have them in a block controller. The further away functionality is from the essence of a block, the happier I become with using a model or library.
jheanly replied on at Permalink Reply
jheanly
Thanks for the excellent detail. I do have a follow up question. I had gone down the helper path, so I have the following artifacts...

- a "helpers" directory within my package
- a file in that directory (let's call it cool_functionality.php)
- the class within the php file is called CoolFunctionalityHelper

If I want to move all of this to a library, the "helpers" directory just gets renamed to "libraries", the php file name can stay the same. What should the class name get changed to? Does it matter? Do I just call the class CoolFunctionality?

Thanks again,

James
jordanlev replied on at Permalink Reply
jordanlev
I would just remove the "Helper" from the end of the class name (so it's just "class CoolFunctionality"). And you'll load it differently -- with the helper you were probably doing this:
$ch = Loader::helper('cool_functionality', 'my_package_handle');

...but with a library, you can't load it directly into a variable like that, so instead you do this:
Loader::library('cool_functionality', 'my_package_handle');
$cl = new CoolFunctionality();


All that being said, if you already have the thing set up as a helper and it's working for you, there's no harm in keeping it that way. The library approach is nice when you don't have a class that you want to load automatically (or maybe you have two classes in there, or maybe no classes at all).
ScottC replied on at Permalink Reply
ScottC
if you don't have a good reason to extend Helper, then the naming convention doesn't matter, you just want one site package(s) install otherwise you may get a "cannot redeclare class" error.

Extending Helper instead of Object had some issues i think though it has been at least a year since then and I can't recall the exact issues I had with it.
ScottC replied on at Permalink Reply
ScottC
hey James, if you can be pretty sure that this block's package will only be installed once per instance of the site you have it installed on, then I would suggest you at least look at the inheritance structure of:

Loader::library('awesome_controller','james_package');  //in your package's onstart method  or register an autoload in site_post.php under config


class SweetBlockController extends AwesomeBlockController{ //which extends BlockController and thus controller


You can then centralize a lot of your methods under AwesomeBlockController which extends Controller(it is loaded in the dispatcher so you don't need to manually load it via Loader) so you get redirect, get, post and all those other methods.

It really all depends, but that is another way :)
ScottC replied on at Permalink Reply
ScottC
the reason why you'd even want to worry about it is if you have an awesome class that extends ADODB_ActiveRecord and you want to reuse that between a bunch of packages, so you'd want to make sure that the class wasn't already defined via passing all requests for it via __autoload or leveraging something else.