-
Notifications
You must be signed in to change notification settings - Fork 824
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
RFC: Namespace of Page #5844
Comments
Leaning towards alternative 1. installer can define a default app namespace. No performance issues around using that vs a hard coded /SilverStripe/Pagetypes/ namespace? Also, any need to not provide Page.php as part of the CMS module? With extensions and hooks easy to modify. Would simpify bootstrapping as you wouldn't even need an app / mysite folder potentially. |
I'm glad that very last option is on the list, because the quasi-dependency of some modules on the I have no numbers to back me up on this yet, but my assumption is that the number of modules that depend on What if instead of registering the app namespace globally, each of these modules required the user inform them of the app namespace, e.g. It may feel redundant if the user has many of these Another thing to think about is why can't we just extend SiteTree? |
The class you describe is called "SiteTree". Moving away from extending-via-subclassing to extending-via-extensions seems like a bigger change and not something that I particularly want to get distracted by for SS4. It might be worth exploring but to date I haven't seen many sites that have avoided any customisations to their Page.php. We'd want to look at the ways in which Page.php is used to customise default pages and/or the ways in which modules provide their own page types. I feel like it would quickly get into "do we really need page types at all?" which gives a sense of the scale of the discussion. |
I'd like to see how possible it is to make BlogPage not extend Page, but instead SiteTree, and if it is possible then what needs to be done to achieve it. If it isn't too onerous we could encourage that as a pattern for other modules that need to create something like BlogPage where they would previously extend Page. It might be a matter of BlogPage allowing itself to be configured by the app to use a particular template etc. But I can imagine it would eventually result in needing to extend BlogPage in your particular app, and then use a trait to share common implementation that the Page template needs access to (menu functionality, common code like includes, analytics etc). |
Anyone want to get some numbers? Off the top of my head blog, userforms, forum, ecommerce, memberregistrations, iframe. It's pretty prevalent.
I think it would probably be easier to do away with custom page types as a pattern for module creation, and instead standardise on a content blocks system, and encourage developers to provide a blogblock, userformblock, forumblock, etc. But "do away with page types" isn't small, and it's beyond the scope of this RFC. We've got enough yak shaves as it is. |
Yeah that is quite a few.
No comment on the blocks system or doing away with custom page types, but I do agree with the thrust of the RFC that there needs to be a not too invasive upgrade path for modules currently extending As to alternative 1, this isn't necessarily an issue, but an implication of the solution is that any code that statically refers to |
We could potentially implement this as a composer plugin, so that composer's autoloader takes care of the aliasing. |
Speaking of smooth upgrade paths 😄 Isn't Put it this way -- most cases it shouldn't cause conflict. If/when it does, SS 4.0 should be capable of being extensible enough to define the fully qualified class name for This helps prevent constraining just to |
p.s. That being said, if we need to configure and/or otherwise tell core where my version of |
You can't do a dynamic |
That was one of the options. It seemed weird to have 1 class that was the exception, but it is the "zero extra coding" option and if we were planning on deprecating it's special use in the future, maybe it's not such a bad choice. |
I like @patricknelson 's suggestion:
|
Or class_alias('MyApp\Pages\Page', 'Page');
class_alias('MyApp\Pages\Page_Controller', 'Page_Controller'); UPDATE: Fixed code |
Other way around Sam ($original, $alias). But yeah, that'd work. Don't forget Page_Controller too. |
Which brings us full circle, but instead of treating the __App namespace as special, we're treating the Page class as special. Since this problem exists with only 2 classes, perhaps a more narrowly applied fix is better. |
Yeah. Think about the alternatives. I've tended have these two rules of thumb which may apply to this situation:
And re: @hafriedlander
My point exactly. The less of this, the better. What's so horrible about just leaving it at Also @sminnee other than |
The advantage of __App is: less chance of a class name clash, able to "redefine" what __App points to (if two different places try and call class_alias you'll get a runtime error), only needs defining once (rather than twice for Page and Page_Controller) Both require changes to modules ( @patricknelson: extra class is Page_Controller. |
Only at the point that modules put their code into namespaces. An unnamespaced module will can keep referencing "Page" and the need to switch to "\Page" is obvious.
Only at the point that modules put their code into namespaces. An unnamespaced project can just define Page directly. If we're expecting that by SS5 the subclassing of Page will be deprecated, perhaps a narrower/simpler solution is best? |
Also, even if we had a bootstrap race of some sort, why not just rely on composer as a standard for not only defining and autoloading our PSR-4 namespaces for "classmap": [
"mysite/code/Some/Namespace/Page.php"
] This would "just work" and doesn't require any extra SilverStripe-specific magic/abstraction. |
Be careful with classmap. Unlike with class_alias, |
I'd be more in favour of Alternative 1 and set __App as something by default in the silverstripe-installer? Although moving modules away from the Page dependency would be nice, I could see this work being quite a big task to do. |
I'm not sure I agree that If you don't know the Simplicity is not brevity. To me, simplicity means something is easy to understand, has few working parts, it's transparent, and above all common. My worry with the |
@unclecheese I'm not sure which approach you are advocating for now. Can you clarify? |
Per my previous comment, I still think the approach detailed by @patricknelson makes the most sense. It just feels the most honest to me, for lack of a better word. We're building an escape hatch, not a long-term solution, so my feeling is that it should look like one. |
Perhaps "bridge" is a better term than "escape hatch," but the point being, we're painted in a corner, and we need a reasonable path to moving forward without completely hosing thirdparty code. Whatever we choose is going to be ugly, so let's not try to paint over it to make a hack look pretty. |
Conceptually doesn't even have to be a escape hatch per se. For the lack of a better metaphor, it's more of a central proxy or funnel or gluing framework/cms code to the developer's code. It's intuitive and, if you need to workaround it, you simply need to know a little PHP and you're [hopefully] set. Edit: Yep - bridge is another good metaphor 😄 |
I generally agree with using the non-namespaced Page as the default value. My only quibble is whether we do this:
Or this:
The former is going to generate more cruft that we have do deal with. Off the top of my head:
In short, we'd introduce a lot of weight that isn't brought in by If we went with the first example, would we be asking developers to write these subclasses themselves, or to have some kind of class generator? The one thing that the former (explicit subclass) option has going for it is that it doesn't require code modifications: we can start using it on some sample projects and seeing what happens. On the escape hatch metaphor: this is one reason why I like the idea of it being a composer micropackage that provides a plugin to its autoloader: it can sit at arms length from the rest of the framework, and be easily removed if & when it's no longer needed. That said, I'm not sure how easy it is to write plugins for the composer autoloader... :-/ |
On the other side of the coin, using |
Maybe I could counter a couple points, at least:
Personally I don't see this as an issue. That's a machine problem, we're here to make developers' lives easier 😎
I could be mistaken (from reading the code) but it looks like the manifest is generated just by looking at what's already on the file system (regardless of if it's ever used) and then once a template is requested, a match from this manifest is then retrieved (i.e. no additional file overhead nor CPU/array access, etc). In other words, seeing up an additional class shouldn't slow anything down in this regard. I think... |
@patricknelson @unclecheese the comments that you reference talk about a number of topics, and so I don't know if what you're referring to is module-specific class-remapping, but if so: isn't feasible for the reasons that Hamish has elaborated on. I'm going to update the original post with some amended options based on the discussion. |
Back from holidays, phew a lot of good stuff to catch up on! One factor which (I think) hasn't been mentioned: I've voted for -B (let devs optionally use subclasses), with the minimum necessary core dev work to allow for this ( Unless devs implement this workaround described in -B, the template folder structure will be confusing by default, right? In my opinion that's the biggest downside. The mixed messages about namespacing your own code might turn off devs from using namespaces in SS4. I don't have a better answer though (other than the shift to a more content block focused model).
|
Slightly related. With my PR in the CMS here: silverstripe/silverstripe-cms#1641 it is also possible to do the following in YML and have no root Page class:
Creating multiple options for the dev as a starting point. |
@Firesphere Does that still allow you to do this? <?php
namespace My\Namespaced\Class
class Page extends \Page {} Ultimately I believe your custom namespaced |
@Firesphere it won't work |
Just thought I'd add my two cents here and mention that it doesn't seem to actually work if you add the classes to The classes are exposed to PHP (i.e. EDIT: yep the manifest doesn't look at |
Just thought I’d drop by to add the method we’re using to overcome this. We have bootstrap.php (probably to be renamed on reflection), included in composer’s autoloader: <?php
use App\Control\PageController as NamespacedPageController;
use App\Model\Page as NamespacedPage;
class Page extends NamespacedPage
{
private static $hide_ancestor = NamespacedPage::class;
private static $table_name = 'SilverStripe_Page';
}
class PageController extends NamespacedPageController
{
} Using |
Firstly, apologies for commenting on what seems to be an issue that has been sorted and referenced multiple times! I have recently upgraded a project to V4 and decided to namespace all of the application code. I ran into the issue However, today I have run into a problem regarding "generic" page types. For some context here is a snapshot of my template and src directory structure ():
I followed the advice given in this thread and created the two classes to sit in the global namespace which I can include in _config.php. When I try and load the application I am faced with Here is the helper file I am including in _config.php use Namespace\Web as App;
class Page extends App\Page
{
private static $hide_pagetype = true;
}
class PageController extends App\PageController
{ } UPDATE: I think another error was causing my issue previously and I now believe I have now gotten the above working. But I now get Uncaught LogicException: Multiple classes ("Namespace\Web\Page", "Page") map to the same table: "Page" Which makes sense. It'd be much nicer if the globally name-spaced Page could be abstract as I don't actually care for it outside of inheriting from it. Is there something I've missed (again)? I don't want two different "Page" class types in the code base (C# partials would be great here!). If I move all of the methods and properties from the name-spaced Page into the global Page I am back at square one again and may as well delete the Namespace\Web\Page? The initial issue that got me into writing this comment was that the generic pages on the site were using the Page type in the global namespace thus rendering the wrong template since there is two Page.ss files in the templates directory. Creating new generic pages in the CMS using the "Page" type and not the Namespace\Web\Page type. Also, one question RE the proposed solution... why is it that the globally name-spaced Page inherits from the name-spaced version instead of the the opposite? |
Warning regarding @kinglozzer fix for getting extensions working that reference the base Page class. It breaks the history page in the CMS |
@torleif That’s weird, page history still works perfectly for us EDIT: I was wrong, it doesn’t work. It works for |
@kinglozzer History will fail to show for all pages that extend from Page. If you extend from \MyNameSpace\Page, you'll be ok. I suspect this is because ReadOne.php in the GraphQL resolver will try to find all classes that extend from the base page hierarchy. We had the issue of many of our pages not having history, as we're running several extensions which extend from Page. Some extensions don't have the option of modifying the code, so I worked on this issue over the last day and came up with a fix that will allow all pages to show up in the history tab: app/_config/config.yml
app/src/extensions/ReadOneExtension.php
I'll try some testing on a fresh install to see if i can replicate the issue with clean code. Edit: I managed to replicate this issue on a fresh CWP install |
Given that the RFC itself is about the namespace of |
In SS4 we're introducing namespaces to all classes. However, for the
Page
class, this presents an issue.The
Page
class is supplied by a project developer as part of their application.The blog module needs to know what namespace Page sits in. But what namespace will developer put their project code into? Generally speaking, code should be placed into a globally unique namespace, which means that there will be no single namespace within which Page sits.
Accepted solution
class Page extends AppNamespace\Page (option 20160729-B)
Developers can create a small helper file with this boilerplate (or include it in their _config.php):
Note that the expectation is that developers write this code themselves rather than have it auto-generated.
Pro:
Con:
$hide_pagetype
. Probably$hide_pagetype
will replace$hide_ancestor
Other comments
In both of these options, it's expected that by SilverStripe 5 at least, we'd no longer have to have module maintainers subclassing Page as a way of providing custom functionality. So, the facilities provided by this RFC won't be long-lived. However, exactly how we do that is beyond the scope of this RFC.
Other alternatives
class_alias() (option 20160729-A)
We can create a class loader that lets us define class aliases, e.g. you could provide this in your _config.php
Pro:
Con:
SSApp\Page
namespace Simpler, but it forces people to bake in assumptions about whether a piece of code is in a project or a library. Since a lot of code starts out as a standalone project and then becomes a library over time, this seems awkward (e.g. a CLI tool that you then want to include as a library into a project as an alternative)set_app_namespace('Sminnee\\DevProject')
syntax. I prefer the former, more explicit syntax.The text was updated successfully, but these errors were encountered: