I'm making a web JSON application, and I would like to make a version router. Like, I could have the path /v0.0/../.. and /v0.1/../..
And I wonder if it is possible not to load every route but just load the route with the good version.
You could check what URL has been requested using $_SERVER global variable before creating routes using $app->get(....
However, this isn't an ideal solution and I think it's not even necessary. The overhead caused by creating routes that aren't used is very small, basically none. Silex uses Dependency Injection (http://pimple.sensiolabs.org/) which makes it very efficient and controllers that aren't used aren't even instantiated.
So I think you don't need to worry about defining more routes that necessary.
In modern web frameworks (Laravel, Symfony, Silex, to name a few), there seems to be a pattern of using a routes.php file or similar to attach URIs to controllers. Laravel makes it a bit easier with an option to use PHP annotations.
But to me all this feels like a bit of a code repetition, and when you are creating/modifying controller logic, you have to keep routes file always at hand. Interestingly, there's a simpler way I saw in several old frameworks, and I used to use this in my old projects as well:
Controller. All classes in src/controllers folder (old way) or all classes in YourApp\Controllers namespace are being automatically mapped to the first part of the URL by adding "Controller" to it. Example: /auth gets mapped to AuthController, /product/... — to ProductController, and / — to default IndexController.
Action. Action is the second part of the URL, and it gets mapped to the method name. So, /auth/login will call AuthController::loginAction() method. If no second part provided, we try indexAction(). Don't want people to access some internal method? Don't make it public.
Parameters. Next parts of the URL are being mapped to the arguments of the method; if there are Application and/or Request type hintings in the argument list, they are skipped so they can be properly injected; we can access GET/POST variables as usual through Request.
Here's the full example, using all these features together:
URL: https://example.com/shop/category/computers?country=US&sort=brand
namespace MyApp\Controllers;
class ShopController extends BaseController {
public function categoryAction(Application $app, Request $req, $category, $subcategory = null) {
echo $category; // computers
echo $subcategory; // null, it's optional here
echo $req->get('country'); // US
echo $req->get('sort'); // brand
}
}
I'm sure it seems to lack some familiar features at first, but all features that I can think of could be easily added if needed — using attachable providers, connecting middlewares, branching controllers to subcontrollers, specifying HTTP methods, and even performing a bit of a pre-validation on the arguments. It's very flexible.
This approach would really speed up the routing creation and management. So besides having all the routes in one file (which is also not always true, considering various providers, using ->mount() in Silex or bundles in Symfony), what are the reasons modern frameworks seem to prefer this way of doing MVC routing over the simpler way I've described? What am I missing?
I'll speak here from Symfony/Silex perspective:
Decoupling. routes.php provide separation of URL mapping and controllers. Do you need to add or change URL? You go straight to routes.php. Very handy if you want to change a lot of them.
Flexibility. Generally, routes.php is a bit more flexible approach. SEO might go crazy and might require you to have route like /shop_{shopName}/{categoryName}/someStaticKeyword/{anotherVar}. With routes.php you can easily map this to your code, but this might become a problem if your routes are directly mapped to the code. Even more, you can have this only controller, you don't need to write controllers for each slash part. You can even have different controllers handling the same URL with different variable parts, for example /blog/[\d]+ or /blog/[a-z-]+ being handled by different controllers (one might generate redirect to another). You might never need to do something like this, but this is just a demonstration of flexibility of this approach - with it anything is possible.
Validation. Another thing to consider is that routes.php provide simple means of validation via ->assert method. That is, routes.php does not only map URLs to controller methods, but also ensure that these URLs match specific requirements, and you don't have to do this in code (which in most scenarios would take a bit more code to write). Also, you can create default asserts for some variables, for instance, you may ensure that {page} or {userId} are always \d+, single line in routes.php that would take care of all usages of {page} or {userId}.
URL Generation. One more feature of routes.php is url generation. You can assign any route any name (via ->bind() method) and then generate URLs based on that name, and providing variables for parts of URL that change. And once we have this system and use URL generator throughout our code we can change URLs as much as we want, but we won't have any need to edit anything but routes.php. And once again - these are flexible names, that you won't have to change everywhere throughout your project once URL has changed and you are not limited choosing the name. It might be much shorter than the URL, or a bit more verbose.
Maintainability. Say, you might want to change some urls (as in example above - from /blog/[\d+] to /blog/[a-z-]+. Also, you might want to keep both of them for some time, and make old one redirect to the new one). With routes.php you simply add a new line and add a todo memo to remove it in some time if you want to remove it later.
Sure, all of this might be achieved with any aproach. But would that be this simple, flexible, transparent and compact as this approach?
Just as note, the standard edition of Symfony is shipped with SensioFrameworkExtraBundle, that allows to use annotations instead of file-based declaration:
/**
* #Route("/")
*/
public function indexAction()
{
// ...
}
In the same manner, you can set the prefix for the whole controller file:
/**
* #Route("/blog")
*/
class PostController extends Controller
{
/**
* #Route("/{id}")
*/
public function showAction($id)
{
}
}
Still, we have to declare it. I think the main reason is that routing requirements are heavily influenced by SEO optimizations. For anything that faces the public you want some keyword-rich URL. The "public" logic organization may also be different, you may have only one controller to deliver all your static pages, still you want those to be /contact, /about...
In some domains, routes can be inferred with conventions. If you create a REST API using FosRestBundle, it will auto-generate routes based on your controllers/actions names on the ressource approach. In general I think the FosRestBundle got a lot of things in their approach, you can easily parse and validate query parameters at the same time:
class FooController extends Controller
{
/**
* This action route will be /foo/articles, GET method only
*
* #QueryParam(name="val", default="75 %%")
*/
public function getArticlesAction(ParamFetcher $paramFetcher)
{
...
}
I always prefer having the routes in a config file rather than using annotations because, firstly, I feel it's more maintainable and easier to have an oversight. It's easier to ensure that you don't have any conflicting routes when you can see them all together.
Additionally, it's theoretically faster. Annotations require reflection, where the application needs to scan the file system and parse each controller to collect the set of routes.
The reason is probably maintainability for the long term. It doesn't matter how the routes are set up but generally, Laravel as a framework focuses on the simplicity and the small things that make the framework great. As long as it is easy to use for the user (web developer in this case), the goal is reached.
You can use a different name for the routes.php file and also use multiple route files, if you have a lot of routes in your application.
You can try and rethink the whole concept to improve it but I don't think that Laravel's routes need really a big change to make it even easier. It's already simple. Maybe it will be changed for the better in the future but for now, I think it's fine.
I often use Ctrl+Click on a method to navigate to its declaration, or RClick -> Find Usages to see all usages of it.
In Laravel you often define in strings which method shall be called. For instance:
Route::get('customer/{id}', 'CustomerController#customer');
I see no way to navigate to the method customer in the class CustomerController automatically here. Find Usages doesn't find this one either.
Is there any way to get it working?
As you said in your question, CustomController#customer is a string. So you cannot click on this string to find out its usages.
However, you can still create a route name for your routes and check for those routes. You can use this route names in your controllers, views and even models.
So using route names would give you tremendous flexibility in development. Just change your method usage in routes.php and it will be reflected in all places.
I'm new to MVC's (this is my first time using one in a real scenario) and I am a little bit confused about how controllers (should) work.
After reading the Laravel documentation, I came to the conclusion that for general tasks like handling loading the sites (different sites are connected together), pages, admin, etc. I need RESTful controllers.
For the simple tasks it worked. It was easy and very fun to use. I had a Route like this:
Route::controller('admin', 'AdminController');
I had functions like
public function getProduct($id)
and it worked. (It was used to get a specific product: ../admin/product/1)
Now I want something more complex. Like
../sites/loadsite/mysite/mypage/mysubpage/123?myoption=yes
How do I do this? How do I begin, how to approach the problem? Do I have to use Route::get() for every single thing or is there a "nicer" way of doing this?
Laravel provides the "Resource Controllers" helper for generating RESTful routes for corresponding controller methods:
http://laravel.com/docs/controllers#resource-controllers
You can use this to easily create the standard REST routes for a given model, or, as shown in the example provided at laravel.com, you can restrict to only certain routes. The table they provide demonstrates how given paths map to given actions / controller methods.
Regarding the example url you give: ../sites/loadsite/mysite/mypage/mysubpage/123?myoption=yes, I'll break the question into two pieces, the url and query string:
Regarding the URL /sites/loadsite/mysite/mypage/mysubpage/123. This would not be considered by many to be a "RESTful" route. Instead of pages and subpages, you should be thinking in terms of models, and sometimes submodels. It is commonly considered best practice to avoid deeply nested routes, which typically means anything more than a single layer of depth: /model/{id}/submodel/[id]
Regarding the query string at the end of the url: ?myoption=yes: Laravel provides access to query string parameters by using the Input::get("Param") function. You do not have to designate query string params in your routes, they can simply be accessed in your controller method.
The "nicer" way is resource controllers - which of course may be combined with route prefixing and filters (e.g. authentication filters), etc.
Inside the methods of resource controllers, you can retrieve additional input (the option in the query string of your example) and process it with validation and whatever you want.
Ok, it's my fault. I've never ever learned programming at a school and that's why i'm always ending up in a spaghetti code. I've always curious about different patterns and tried to understand them at least in a basic level.
MVC is my worst fear and i think i'll be never able to use it's advantages because of i don't understand it's fundamentals.
My actual question/problem looks like:
The front controller calls a 'Core' class which is doing some initialization then it calls the actual controller with the correct action/parameters. The controllers always extending the 'Core' class so i can acces it's variables, etc. They're working nicely together but here comes my real problem.
Some kind of methods (getting a database entry in most of the cases) are required in different cases. (e.g. a product needs it's manufacturer)
In this scenario i have two (bad) choices:
Inject the required method into the 'Core' class so it's getting bloated over time
Inject the required method into the actually called controller so i will end up a redundant codebase
I see a lot of possible problems in my approach:
Controllers are always extending 'Core' class
'Core' controller holds the database object so without it i cannot access my Db
Database functions (e.g. getting a product) are in the controllers but i cannot access them because they're always calling 'Core' first (extending problem again)
Please tell me:
Where is the biggest problem in my approach and where can i correct it?
Note:
Please don't treat this as a general question, i think this is an answerable thing. If you need some clarification, please ask for it and i'll try to lighten up things.
Thanks for your precious time, fabrik
Your data is represented to your Controller and View through the Model. The Model may be supported by a Repository but depending on the overhead you might want to provide your database access in your Model. The data architecture should be similar to this:
(Repository<===>)Model<===>Controller--->View
Your biggest problem is having the "Core" class, get rid of it asap.
By the way, the FrontController is not the only way to do things MVC things either.
Your second problem is that controller deals with database, it shouldn't. I suggest you use some abstract data layer, which you use only in your models. And controller should deal only with models, it shouldn't care how models get their data persisted and fetched.
Look into using a DI framework to automatically inject an instance of a repository into your controllers (or even better, a proxy class). Try to keep business logic outside of your controllers, but rather, refector it out into a helper or proxy class.
I tend to split my logic up into views => controllers (just for interaction between business later and view) => business logic => models (DTOs) => low-level data access at a minimum.
Also, if you need common helper functionality in your views, perhaps create several extensions to help.