I want to display completely different layouts for users in different roles on the root url of my application. I am currently achieving this using the following lines in bootstrap.php.
if (Auth::instance()->logged_in()){
Route::set('default', '(<controller>(/<action>(/<id>)))')
->defaults(array('controller' => 'profile','action' => 'index',));
}
else{
Route::set('default', '(<controller>(/<action>(/<id>)))')
->defaults(array('controller' => 'welcome','action' => 'index',));
}
What is the best practice to achieve this in Kohana? Is it ok to add more lines for different roles in bootstrap.php.
Thanks
you should consider using lambdacallback-route-logic
If allows you to modify the requested URL dynamically and much more cleaner than writing something in bootstrap.php
Why dont change basic template in ONE controller (and using the same route)? I think, your controller code doesn't differs if user logged in or not.
I do it like this:
Create an abstract class Controller_Rolebased where in before() method you can implement Role checking.
And then for example:
class Controller_Profile extends Controller_Rolebased
{
protected $_accept_roles = array('user', 'admin'); // this array Controller_Rolebased class will use in before method.
Related
Here is the problem.
I've got a module called Contact. It lives in a directory called modules/Contact. I registered a namespace Contact for the module in app/Config/Autoload.php. Everything seems to load fine. Now I want to use a custom validation method to validate input coming from a form field. My question is about the best way to do it. My approach is as follows: According to CI4 documentation, Validation class loads App/Config/Validation.php when used for validation. In the file there is public property called $ruleSets, which points to an array, it contains names of classes with validation rules. I want to add my own class with validation methods to this array. According to CI4 documentation on validation, configuration classes, and their public properties in App/Config/ can be updated using registrars. So, I crated one such registrar, a class, in my Contact module. It lives in Contact\Config\Registrar.php . The class has public static method Validation, which returns an array with an updated $ruleSet. The new value is an array and contains all the standard validation class names + my validation class name. The trouble is public static method Validation I added in my registrar seems not to change the value of $ruleSet defined in App/Config/. Contact/Config/Registrar.php is loaded, but Validation method it contains is not called. So, what's the best method to add custom validation rules without having to updated files which live beyond my module? There is very little on registrars in CI4 documentation. Have I misunderstood how they work and how they are supposed to be used?
I had the same problem and my solution was to extend the validation class (better solutions are always welcome).
For example I wanted to write my own Auth module and use my own set of rules. My custom validation class lives under acme/Auth/Config/AuthValidation.php
<?php
namespace Acme\Auth\Config;
use Config\Validation;
class AuthValidation extends Validation
{
public $login = [
'username' => 'required',
'password' => 'required',
];
}
This way all validation rules in your main app are inherited.
And you would use this code in your controller to validate:
<?php
namespace Acme\Auth\Controllers;
use CodeIgniter\Controller;
use Acme\Auth\Config\AuthValidation as AuthValidation;
class Auth extends Controller
{
public function login()
{
//Instantiate the new AuthValidation class
$authValidationConfig = new AuthValidation();
//Since the validation class is a config for the validation service we set it for the validation service
$authValidation = \Config\Services::validation($authValidationConfig);
//Set the login rule from our AuthValidation class
$authValidation->setRuleGroup('login');
//Validate the request against our login rule
if($authValidation->withRequest($this->request)->run())
{
return 'Success';
}
else
{
return 'Failure';
}
}
}
It may be not the best solution because I'm pretty new to CodeIgniter but this is how I found my way around it.
Sidenote: The main problem seems to be that the class passed to the validation service constructor has to be an instance of "Config\Validation" so I had to extend it. But there might be other ways around it.
Feel free to correct me :)
I'm planning to organize my controllers in sails using subfolder but I'm not sure how to do it. When I tried using like admin/PageController.js and connect it with the route I keep getting a 404 error.
You can definitely do this. The trick is, the controller identity is its path, in your case admin/PageController. So a custom route in config/routes.js would be something like:
'GET /admin/page/foo': 'admin/PageController.foo'
The great thing is, automatic actions still work, so if you have an index action in the controller then browsing to /admin/page will automatically run it.
You can also create controllers like this with sails generate controller admin/page.
Edit
Since commit 8e57d61 you can do this to get blueprint routes and functionality on nested controllers, assuming there is an AdminPage model in your project:
// api/controllers/admin/PageController.js
module.exports = {
_config: {
model: 'adminpage'
}
}
or this:
// config/routes.js
module.exports.routes = {
'admin/page': {
model: 'adminpage'
}
}
Old Answer
Your options
Defining explicit routes to your grouped controllers in config/routes.js.
Look at Scott Gress' answer for more details.
(If you are a bit adventurous) As i had the exact same requirement for a project of mine I created a Pull Request on Sails that allows you to override the model - controller association. You could install it via
npm install -g git://github.com/marionebl/sails.git#override-controller-model
Assuming it is the api/models/Page.js model you want the blueprint methods for on api/controllers/admin/PageController.js you then could do:
// api/controllers/admin/PageController.js
...
module.exports = {
_config: {
model: 'page'
}
}
Explanation
While generating/creating grouped controllers like this is perfectly valid an possible, you will not get the default blueprint routes you'd expect for controllers accompanied by models with the same identity.
E.g. api/controllers/UserController.js and api/models/User.js share the same identity user, thus the blueprint routes are mounted if they are enabled in config/blueprints.js.
In fact at the moment it is not possible to group models into subfolders in a valid way. This means you won't be able to create a model that matches the identity admin/page of your controller api/controllers/admin/PageController.js - the blueprint routes are not mounted for PageController.
The source responsible for this behavior can be inspected on Github.
I made a diagram that shows how implicit routes, explicit policies, nested controllers, singular models and nested views are related. It does not show an overridden model-controller association as described by #marionebl.
It was mostly an exercise for me to understand this topic better, but I hope it helps somebody else too. Please let me know if I made any mistakes:
Thanks merionebl, its work fine for me and I want to share with all guys my answer derived from merionebl answer.
/config/routes.js
'get /admin/user' : {
controller: "Admin/UserController", action: "find",
model : 'user',
},
My aim is not repeat answer just have upgrade and clear example.
Thanks
I need to perform a search on several entities with the same string then order the results.
I've heard/read a little about FOSElasticaBundle, would this bundle be able to do it? It seems (to me) to have almost to much features for this purpose and I'm not sure it could run on a shared server (hostgator).
The other solution I can think of at the moment is doing the search "manually" (by using join and union) but I'm wondering where should I put such a function: in an existing controller, a new one, a new bundle or somewhere else?
I'm worried as well that this manual solution could come to a cost, especially on some non-indexable fields.
You would do custom entity repositories. Check out the docs. Basically this extends the default FindAll, FindOneBy, etc.
You would have a function like so:
class MyEntityRepository extends Doctrine\ORM\EntityRepository {
public function findByCustomRule(){
//this is mapped to your entity (automatically adds the select)
$queryBuilder = $this->createQueryBuilder('someAlias');
$queryBuilder->orderBy('...');
//this is mapped to any entity
$queryBuilder = $this->getEntityManager()->createQueryBuilder();
$queryBuilder->select('...');
//result
$result = $queryBuilder->getQuery()->getResult();
}
}
This class is defined in the doctrine mapping and lives inside the Entity folder.. Check the docs out and you should get a basic idea.
I can't seem to store additional data in a separate contentpart attached to User. I have done the following:
Created a module
In the module I created a Model for ProfilePart and ProfilePartRecord
In the migration I created a table for ProfilePartRecord (from type ContentPartRecord)
In the migration I altered the typedefinition for User, by setting WithPart ProfilePart
I created a driver class, that has 2 edit methods, one for get and one for post (code snippets are below
I also created a handler that adds a storage filter for profilePartRepository of type ProfilePartRecord
Module Structure
Drivers/ProfilePartDriver.cs
Handlers/ProfileHandler.cs
Models/ProfilePart.cs
Models/ProfilePartRecord.cs
Views/EditorTemplates/Parts/profile.cshtml
Migrations.cs
Placement.info
Since I think the issue is in the Driver. This is my code:
Is it going wrong because the part is attached to User? Or am I missing something else.
public class ProfilePartDriver:ContentPartDriver
{
protected override string Prefix
{
get { return "Profile"; }
}
//GET
protected override DriverResult Editor(ProfilePart part, dynamic shapeHelper)
{
return ContentShape("Parts_Profile_Edit", () =>
shapeHelper.EditorTemplate(TemplateName: "Parts/Profile", Model: part, Prefix: Prefix));
}
//POST
protected override DriverResult Editor(ProfilePart part, IUpdateModel updater, dynamic shapeHelper)
{
updater.TryUpdateModel(part, Prefix, null, null);
return Editor(part, shapeHelper);
}
}
I have used Skywalker's blog. There is one chapter about registering customers by using the User and adding your own content parts to it. Worked nice for me.
First of all - is your ProfilePart editor shown at all when you go to Dashboard and edit a given user? I noticed you're using Parts_Profile_Edit as a shape key, but actually use EditorTemplates/Parts/Profile.cshtml as a template. It's perfectly correct, but note that Placement.info file uses shape keys, so you have to use Parts_Profile_Edit as a shape name in there. Otherwise it won't get displayed.
Second - have you tried debugging to see if the second driver Editor method (the one for handling POST) is being called at all?
Like Bertrand suggested, I'd look into one of the existing modules that work (afaik there is one for user profile in the Gallery) and see the difference. It might be something small, eg. a typo.
Let's say I want to make a system which can afford a multilingual web project. The system will consist of the modules that are put in Kohana's standard directory modules. Let's say that the standard access to the particular language can be done via lang parameter (i.e. somesite.com/en/somepage). The problem is that I have to repeat myself in defining my modules routes prepending each uri with (<lang>). Is there any way to avoid that? I thought about a separate language route declaration (for example in bootstap.php file), but I guess it won't solve the problem.
It's all about Kohana 3. Thanks to all.
UPDATE:
I think that the way suggested by The Pixel Developer is what one need if some part of the rule in route repeats everywhere.
Move up a level and extend the route class.
http://github.com/kohana/core/blob/master/classes/kohana/route.php#L69
public static function set($name, $uri, array $regex = NULL)
{
return parent::set($name, '(<lang>)'.$uri, $regex);
}
Not tested, but that's the general idea.
If lang is required in route, why don't you just put it in the default route? Surely that's the easiest way to go about. Something like:
Route::set('default', '<lang>(<controller>(/<action>(/<id>)))', array('lang'=> '[a-z]{2}'))
->defaults(array(
'controller' => 'somepage',
'action' => 'index',
'lang' => 'en',
));
Where lang is any 2 letters alphabets which defaults to 'en'.