Love Fuel?    Donate

FuelPHP Forums

Ask your question about FuelPHP in the appropriate forum, or help others by answering their questions.
Extending Controllers & Routing
  • Hello,

    I have a multisite Fuel app where multiple sites use the same controllers, each website has its own configurations files, theme and public folder base on a ENV variable.
    Of course, I reached a point where each site has different needs, so I though that extending the Controllers I already had was the best idea. it works pretty well, except for a little thing that bugs me.

    Let's say I have a controller named HomepageController with an action_index() method. Site A doesn't have any particular need, so I don't need to extend this controller.
    Site B, however, needs a different action_index(). So I created a new controller called HomepageController_SiteB that extends HomepageController and rewrote the action_index(). Great it works!
    The problem concerns routing:

    - If I type sitea.com/homepagecontroller/index, I reach my homepage, that's good.
    - If I type sitea.com/homepagecontroller_siteb/index, I reach the index function of my Site B controller, that's not good.

    - If I type siteb.com/homepagecontroller/index, I reach my parent homepage controller, that's not good.
    - If I type siteb.com/homepagecontroller_siteb/index, I reach Site B's specific homepage, that's good.

    How can I make sure that each site can only call the HomepageController it uses?
    Hope my question is clear...

    Thanks in advance!
    Alex
  • HarroHarro
    Accepted Answer
    The short answer: you can't. A Fuel app is a Fuel app, there is no way you can sub-divide it, for what you want to achieve, your architecture is wrong.

    You should create the following folder structure:
    - sitea
      - app
      - public
      - ...
    - siteb
      - app
      - public
      - ...
    - fuel
      - core
      - packages
      - vendor
      - ...

    where both "sitea" and "siteb" contain a Fuel app installation, but without the fuel folder. have the fuel folder separate, and change path constants in the oil and index.php files so that they point to the new location of the fuel folder.

    Now you have two separate apps that run of a single fuel core.

    Next step is to create a solution for shared code, stuff you need to share between your site apps. There are several solutions for that.

    What most people do, and what the "designed" solution is, is using packages, and put the classes you want to share between apps into a package. This can be a package in the fuel/packages folder, but you can also setup a separate shared packages folder so you can keep framework extensions and app extensions separate.

    In the app you can then create your controllers, that extend a base controller class in the shared package, and not have any influence of other apps that might do the same.

    I've also seen people uses traits, so define a trait for every action method in a package, then in the app controller, just include the traits you need. This obviously works best if you have a lot of idential functions.

    And you can do the same for modules. We have a long list of modules which we share amongst applications, for example a module "admin", which provides all user and permission management for an app. We just have to add it to the app, make sure the correct theme files are present to style the module, and we're in business.
  • Hello Harro,

    Thank you for a very helpful and complete answer.
    I knew that my folder structure was wrong, but I'm working with legacy code so i was looking for a easier solution. Unfortunately it doesn't exist...
    Looks like it's going to ask for more work than I thought it would!

    Thanks again!
    Alex
  • It's not really that much work I think, the biggest job is to figure out where to move the files to, and renaming them (which changes the namespace and classname).

    Even for a sizable application that should be doable in a day i'd say.
  • I did some progress quite quickly so I think it won't be that long finally.

    I'm having a little trouble with extending Core classes Fuel and Config from my package. Reading the doc, I see that it's not possible even when loading my package before Fuel::init().
    How would you do that with the folder structure you suggested on your first post?

    Edit: Ok I managed to do this by removing the package's Namespace from Fuel and Config and by adding these classes in app/bootstrap.php and not in my package's bootstrap.php.


    Thanks in advance,
    Alex
  • Just out of curiosity: why would you want to extend those classes?
  • Hello,

    I got rid of the Config class extension. It was rewriting the save function so it could save a config file in a specific folder depending on the ENV var.

    Now for the Fuel class, it extends the init() function with language acronym detection in the url and setting it as a constant. It will also create routes for each page slug in the database. Please note that I'm working with legacy code that was first developed with Fuel 1.2 then upgraded to 1.7, so it probably made sense back then...
    My goal is to get my code to work with Fuel 1.8, and then clean up all the mess.

    Any advice is welcome!

    Going back to the initial problem, it starts to work pretty well except for the Sessions. Every time I refresh the page, I get a new cookie file. Any idea why?

    Thank you!
  • It's probably better to do the language detection and stripping in your app bootstrap.

    Not sure when it made sense to do it that way. The fun of Fuell is that a lot of roads lead to Rome. ;-)

    As to sessions, check what the expiry of the cookie is that the browser receives. In most cases, disappearing cookies are caused by incorrect time settings, which can be visible by receiving a session cookie with an expiry timestamp in the past (causes immediate expiry).

  • Yes, that's the beauty of frameworks. I had the chance to learn a lot about how Fuel work these past few days, thanks to the previous developer :-)

    Regarding the cookies, I checked the expiry date. It's set to 0 in my config, I saw 2018. Alright that's good.
    But when checking the hour, I saw that it will expire at 1:51, but it's 3:51 right now... huh that's strange. Open the cookie, check the created timestamp and it was created at 1:51 UTC, that's 2 hours early.

    Check the default_timezone in Fuel's config, set to 'Europe/Berlin', check the server timezone in php.ini, set to 'Europe/Berlin'...
  • Berlin is at UTC+2 at the moment, so there should be a 2 hour difference. Cookies are timestamped in UTC, not in local time.

    And what time and timezone is the server running? Linux clocks run in UTC with a set timezone (which should ideally be Europe/Berlin too), Windows runs local time, so there you have to be very careful.
  • btw, if "expiration_time" is set to zero, the framework will convert that to a two-year expiry, so 2018 should be correct.  That loss of cookie doesn't seem to be the problem.

    What is "rotation_time" set to? The default there is 300, meaning every 300 seconds the session id rotates, which also means you get a new file.
  • rotation_time is set to 300. The thing is I double checked the config with my previous app, and it's exactly the same. There's no reason for it to create a cookie everytime. Plus the cookie files are still in my folder, it just adds a file on every refresh.

    that's weird, could this be linked to changing the folder structure?
    Here's my config:

    'cookie' => array(
    'expiration'  => 100000000000000000000,
    'path'        => '/',
    'domain'      => null,
    'secure'      => false,
    'http_only'   => false,
    ),

    and for session:

        'driver'            => 'file',
        'expiration_time' => 0, 
        'file'                => array(
            'cookie_name'        => 'vipfid',
            'path'                =>    session_save_path(), 
        ),

  • Don't see an immediate issue.

    Nothing in the application log file (set the logging to L_ALL if needed)? Anything in the webserver log?

    Do you see a new session id arrive in the browser too?

    Are the rights on the session files ok? If the app can't see or open the session file, it will also generate a new one.
  • Hello,

    Nothing special on Fuel nor PHP logs...
    I spent some minutes today doing some tests, and I have the feeling that the problem comes from the fuel-cart package. Are you aware of any issues regarding sessions with this package and Fuel 1.8?
    On the cart's config, it works when I set the driver to Cookie, but it doesn't work when setting to Session.

    Thank you.
  • Ok, so the problem comes from the class Fuel\Core\Session_File line 371. In this line a trailing slash is added to the cookies path. This causes the test at line 308 to fail on Windows, I guess because the directory separator is antislash.

    Changed line 371 to $item = realpath($item).DS; and it works.

    Edit: just saw on Github that this was corrected on May 10.
  • Ok, good to know you found it. Guess it pays to stay up to date with our releases... ;-)
  • Haha yes, but I used composer to create my project with the line specified in the installation page: composer create-project fuel/fuel:dev-1.8/master --prefer-dist .

    Did that again today on a new folder to make sure I didn't do anything wrong, and the file.php is not at the last version ;-)

    Just remembered I have to adapt the composer.json file to match my new directory structure...
  • Since 1.8 we use tagging, so it's best to update your composer.json with the one from 1.8 (https://github.com/fuel/fuel). It uses "1.8.*" so it automatically pulls in new versions if present.
  • I have the same composer.json...
  • Then it should pull in the 1.8.0.1 update?
  • Grmfff... The new tag wasn't pushed for some reason. Sorry about that, uit should update now.
  • Thanks Harro!
  • OK so I got it all working now.

    I have one more question :

    1. Imagine that I have three sites: A, B and C.
    What if I want a Controller to only be available in A and B?
    Should I rewrite the routes of this specific Controller in C to redirect to another page (404 or root) ?


    Thank you!
  • If you have controllers that you share between apps, there are three options:

    One is to use a module. If you do, the entire module is shared as-is, and you have to indeed resort to either routing or access rights checking to prevent access to a controller. You mainly use modules for functionality you can slot-in.

    A second is use a package with base controllers. Now, packages are not routable, and therefore can not contain controllers directly. But they can contain base controllers, which is just a fancy name for a normal class. So you can have a package "shared" that contains a class "\Shared\Controller\SomeName", and a controller in app A and B that extends this class  (and thereby adding it's functionality to the app.

    And a third option is to use traits. Again, define them in a package, and include them in your app controllers where needed. It is a bit more work, but it gives you more granularity over what functionality you include and what not.

    The route to take depends on your requirements really.

    Oh, and in all cases I should use themes instead of stand-alone views, since they can also be shared (but may have an override in the app). Easier than duplicating all views.
  • Thank you for the reply.

    I created a package, and moved my controllers and models inside this package. Declared all the classes in the bootstrap.php. Everything works.
    Now, when you say "packages are not routable", I can definitely access methods defined in these controllers. Probably because I kept them in the root namespace.

    Let's say I moved my base controllers to my package namespace. Now if I want to use these controllers, I should extend them in my different sites, right? Does it mean that I will have empty classes if I don't want to do any change? Hmm...
  • Packages MUST have a namespace, otherwise autoloading doesn't work. You can alias them to the global namespace (like some packages do) and then yes, your controllers become loadable too. But that is not exactly best practice.

    And yes, if you leave them in the namespace, the only options are extending or using traits, for both you need a dummy or empty controller in your app.

    If you don't want to do that, you either have to resort to null routes (route to a 404 controller), or use an access system (which then would generate a 403 if no permission is given).

    Both have a potential security risk.

    The package solution is basically a "deny all, allow some" system, you explicitly have to make something available, while the route or permission solution is a "allow all, deny some" system, where you might give a user access to something he/she shouldn't have, because for example the package got additional functionality but you forgot to add new null routes...

Howdy, Stranger!

It looks like you're new here. If you want to get involved, click one of these buttons!

In this Discussion