Extending core classes

Permalink 1 user found helpful
I read a couple threads about extending the core and basically it comes down to creating a copy of the conrete/*file* in the appropriate folder in the base directory.

What I want to do is extend the core without replacing a file in its entirety. I don't think this is possible with the current architecture.

I develop a lot nowadays using the Kohana framework. It's structured so that every class (e.g. BlockTypeList) is an empty proxy class that extends the actual core class (e.g. C5_BlockTypeList). This allows you to override the proxy class in your application and alter specific methods rather than duplicating core class in its entirety.

Is this something concrete5 devs would consider doing to the core? I realize it would be quite a bit of work, but it would make for greater flexibility with less hassle (I think).

Hell, if the devs are interested, this is something I'd be willing to take on (I probably will anyway) if it would actually be used.

View Replies:
Ricalsin replied on at Permalink Reply
Ricalsin
To me, it would seem your request is not worth the effort since C5 already allows several significant scenarios that essentially do what you are asking. One, you can copy the existing c5 core file into the base directory and begin "extending" your code from there (but, that's not to be confused with truly "extending a class"). Two, you can truly extend classes by adding those files to the base directory (which will never be hindered in any c5 upgrade) and in this way you are tapped into the parents - a seamless integration of your custom code.
12345j replied on at Permalink Reply
12345j
well, for example packages extend package, like MyPackagePackage extends package- is that what you're asking? you can do that with a lot of files.
Ricalsin replied on at Permalink Reply
Ricalsin
...we're saying the same thing. The possibilities that C5 enables seems to answer the OP.
12345j replied on at Permalink Reply
12345j
woops, didn't mean to reply to you.
simshaun replied on at Permalink Reply
It seems to accommodate several scenarios yes, but I don't think it accommodates the one I'm after. Based on my understanding, packages can only do so much, and altering core is not part of it.

I don't want to copy the core file into the base directory and modify it. That completely negates what I'm trying to accomplish.

Perhaps an example would be best.
---------------------------

Let's say I want to add a method to the BlockTypeList class.

Currently, and correct me if I'm wrong, you're saying I have to copy /concrete/models/block_types.php to /models/block_types.php and then just modify the class. Well, now I have an issue of my "custom" file containing >700 lines of code that I really don't need or want to modify. Not only that, but any future concrete upgrades which fix things in the original file now become something I have to worry about reflecting in my "custom" file. That's something I as a developer should not have to do when all I'm doing is adding an additional method to the class.

In my proposal, those issues are mitigated because my "custom" file simply extends the "core" file, rather than replacing it.

For example:

/models/block_types.php
<?php
class BlockTypeList extends C5BlockTypeList {
    public function foo() {
        // Does something special
    }
}



/concrete/models/block_types.php (system knows to use my class instead of this one)
<?php
class BlockTypeList extends C5BlockTypeList {}



/concrete/models/c5/block_types.php
<?php
class C5BlockTypeList extends Object {
    ... all the contents of concrete5's current BlockTypeList class go here ...
}


Maybe I'm just being stubborn, but I think doing it that way is much better than the current way.

There would be a major hurdle to overcome in accomplishing this though. Each class would need to be separated out in to it's own file so that the skeleton definitions of the rest of the classes in /concrete/models/block_types.php don't have to be copied in to the developer's "custom" file.
12345j replied on at Permalink Reply
12345j
I know that currently anything that extends object can be extended, and I think some other things can as well. package was just an example, lets say you wanted to extend groups- the file would be MyGroupList extends GroupList- which is what you want to accomplish, right?
Ricalsin replied on at Permalink Reply
Ricalsin
No, you've got it wrong. What you want to do is easily doable within the C5 system as is - you just need to correct your understanding of how classes are extended.

Your Goal: To add your custom methods to an existing class by creating a new class which extends the old class.

Syntax: If the C5 core class is "BlockTypeList" and the new class you want to create is "C5BlockTypeList" then the proper syntax is:
class C5BlockTypeList extends BlockTypeList {}

Your sentence of: "the system knows to use my class instead of this one" is incorrect. In the above syntax I gave the system would simply use the C5 core and then add your extension to that class.

The system WOULD know to load a base directory file and use IT instead of the same-name file in the concrete/ directory - hence overriding it (with all of your legitimate concerns regarding updates which you mentioned).
simshaun replied on at Permalink Reply
I know I can create a class (MyBlockTypeList) and extend BlockTypeList until pigs fly. Again, not really what I'm trying to get across, but I'll run with it:

So I create MyBlockTypeList which extends BlockTypeList and I override BlockTypeList::getAvailableList(). How do I then get the core to use my instance of "MyBlockTypeList" anywhere it currently uses "BlockTypeList"?
Ricalsin replied on at Permalink Reply
Ricalsin
Well, you may know it, but your syntax and statements that you used led me to believe otherwise.

You want to override a c5 core class so that you can change some of the methods, but you don't want to make those changes in a new file and place it in the base directory because of upgrade issues and possible conflicts - which would probably exist in your proxy scenario too. I haven't heard of anything being discussed about a proxy class in order to do what you wish to do. The value gained over existing means seems - to me - to be too little for the effort involved.

But maybe I am wrong and a C5 core member might be able to better address your concerns. Take care.
andrew replied on at Permalink Reply
andrew
I see what you're saying. That's actually a pretty good solution. Correct me if I'm wrong, but I don't think it actually involves any backward compatibility problems, just a lot of core changes, right? For example, you'd have to rename Page to CorePage, but keep Page around – but then you'd be able to fork Page in your overrides directory (the same way you can now) but instead of having to duplicate everything, Page can just extend CorePage the same way it did before. Many little changes to make in many little spots, but no backward compatibility problems – right?

That's a pretty clever solution. We actually let you fork and extend helpers but no other classes – but honestly that way of doing it is nicer from a code perspective than our custom helper fix.

Obviously, if this involves backward compatibility problems we can't really do it, but it reads to me like it wouldn't.
simshaun replied on at Permalink Reply
Sorry for the late reply. I've been busy as of late.

@andrew, that is exactly what I'm proposing. I don't have any experience developing with C5 yet, so I can't say for sure whether or not it's backwards compatible. I would think that there would be no issues, assuming the names of the new proxy classes remain the same as the current class names.
Shotster replied on at Permalink Reply
Shotster
> That's a pretty clever solution. We actually let you fork and extend helpers but no
> other classes – but honestly that way of doing it is nicer from a code perspective
> than our custom helper fix.

Yeah, I like it too. It would make extending the core simpler, more flexible, and more intuitive. Any chance it will make it into a future release? It doesn't sound like it would be too painful to implement -- especially if the dev community is willing to help.

-Steve
andrew replied on at Permalink Reply
andrew
And to everyone else: I don't think simshaun is really saying you _can't_ fork core models/libraries in concrete5. I think the problem is just that, if you do, you can't fork and _extend_ the original seamlessly (to only modify the one or two API methods you need to modify, or to add a couple API methods.) Instead, you have to copy the entire file, and remember to diff and apply any new core file changes to your own custom classes, any time you upgrade.
bagonyi replied on at Permalink Reply
The workaround I found for this:

Copy the file from /concrete you want to extend to let's say /C5 to the same level, rename the class(es) to C5_{original classname} then create a file with the same filename on your level, which has class {original_classname} extends C5_{original_classname} { }. This way you either do a require_once() at the top of the file at your level, pointing to the file in C5, or you write your own custom autoloader, and register with spl autoload register.

For example, let's say you want to extend the Page class:

1. copy the file /concrete/models/page.php to /C5/models/page.php
2. prepend the classname with C5_ in /C5/models/page.php, so it should look like:

<?php
// /C5/models/page.php
class C5_Page extends Collection {
//...
}


If there are multiple classes in the file IMO you should rename all of them, even if you don't extend those, this way you can avoid classname collisions.

3. create a file named page.php at your level: /models/page.php
4. require the file /C5/models/page.php and extend that class.

<?php
// /models/page.php
require_once DIR_BASE . '/C5/models/page.php';
class Page extends C5_Page {
    public function getSomething() {
        // ...
    }
}


This way you can extend the core, but the drawback is that you have to copy these files to /C5 every time you upgrade the core to make sure you always extend the latest version of the core class.