I have a many-to-many relationship created using fuelphp's ORM.
The pseudocode for the relation looks like this
class MyModel extends Model
{
protected static $_many_many = [
'relatedmodel' => [
'conditions' => [
'where' => [
['ctime', '>', DB::expr(('now() - interval 1 week'))],
],
],
]
];
}
The idea here is that I only want the relationship to look at newer relatedmodels that were created in the last week.
However, this obviously won't work because of a php language constraint - an expression is not allowed as a field default value.
How can I get the desired behavior in FuelPHP despite that constraint?
The work around for the language constraint here is to use Fuel autoloader's public static _init() function to set the value. This gets called automatically when the class is loaded by the autoloader.
http://fuelphp.com/docs/general/classes.html#/init_method
Related
So I just started a Laravel Project with Breeze, and I wanted to change the default table users , the problem is it didn't work, I did my research for days and I didn't get any successful result
I will try to explain what is the problem and what have I tried so far.
First, I created a new table called users_data, and this table, is completely different than the users table.
The fields that users_data has, are for example: name_value, password_value, age_value, email_value, etc. (I have to mention too that for the table users_data, it doesn't use a migration, because I already have an sql file, and added it directly to the db (I already have tables created, with primary keys, and foreign key, so i couldn't do the migration because it would take me a lot of time), and without the migration I can still get the data, so I don't think it could be this the problem).
Actually I'am using Breeze, however, I used Auth scaffolding (PHP artisan make: Auth) too
What have I tried:
After several days of search, first I have created a new Model, called UsersModel, the content of this is the same as User Model however what I change is:
protected $table = 'users_data';
protected $fillable = [
*name_value*,
*password_value*,
];
and an extra function to override the default password of breeze or Auth (I guess):
public function getAuthPassword()
{
return $this->password_value;
}
next I went to conf/auth.php
there I specified the Model:
'providers' => [
'users' => [
'driver' => 'eloquent',
'model' => App\Models\UsersModel::class,
],
and the table to use:
/*
|--------------------------------------------------------------------------
| Authentication Table
|--------------------------------------------------------------------------
|
| When using the "Database" authentication driver, we need to know which
| table should be used to retrieve your users. We have chosen a basic
| default value but you may easily change it to any table you like.
|
*/
'table' => 'users_data',
after this I went to the view login.blade.php, and changed only the email input (for what I read on different pages, changing the password input will cause different problems, because I would need to make a lot of changes to make it work so the best idea is to override it with getAuthPassword, specified in the model):
new name input:
x-input id="email" class="block mt-1 w-full" type="text" name="name_value" :value="old('name_value')" required autofocus />
After all this I went to LoginRequest (the validation for the login), where I replaced email for name_value
I tried to debug this:
dd(Auth::attempt($this->only('name_value', 'password'), $this->boolean('remember')));
and returns false
I noticed that there's a function in vendor/laravel/ui/auth-back/AuthenticatesUsers
called username(), that returns 'email'
when I saw that I remembered a page that said that this function could override too, so I changed the return value to name_value, and it doesn't do nothing
last, just to clarify,
I don't need the Register site I only need the login page, so for that in the $fillable I didn't add all the columns of the database, just the ones that I need to log in (name_value, password_value)
If anyone could help me and guide me it will be great, because I'am running out of ideas (I could do it with PHP alone, however, I need the ->middleware ['Auth], is there a way to activate the middlware if the user exists?)
So you might have a model named users_data.php. Go inside it and change the code to something like that.
STEP:1
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Foundation\Auth\users_data as Authenticatable; //Add this line for Auth.
class users_data extends Authenticatable //Extends child from Authenticatable parent class obj.
{
use HasFactory;
protected $fillable = ['column1', 'column2', 'column3', .....];
}
STEP:2
Go to config/auth.php. You may found something like this below.
'providers' => [
'users' => [
'driver' => 'eloquent',
'model' => App\Models\User::class,
],
// 'users' => [
// 'driver' => 'database',
// 'table' => 'users',
// ],
],
Change the 'model' => App\Models\User::class, to 'model' => App\Models\users_data::class,
This is the main character in this drama that putting your application to users table by default.
STEP:3
Comment off the User.php so that no future conflict create.
Now your application has been diverted to your desired table and ready to login.
I don't think it's good practice to be editing vendor files. We don't push them to version control so other developers won't have your changes. Also, Laravel already has a way to override the username value without editing the vendor files.
Just use the trait in your auth controller like this:
public class MyLoginController {
use AuthenticatesUsers; // or you can also use ThrottlesLogins trait
// then override the username function here
public function username() {
return 'name_value';
}
}
To override the password you can define this on your User model:
public function getPasswordAttribute() {
return $this->attributes['password_value'];
}
public function getAuthPassword() {
return $this->password_value;
}
I haven't tested this but based on the docs this is how you should do it. Also make sure to read this Laravel doc.
I am trying to use the standard Laravel auth related functionality. I have the standard login, register, logout, etc... working fine. But my problem is that whenever I go to use functions like Auth::user() this is returning the GenericUser model rather than my own App/Models/User.php model. The problem is that my User's model has some foreign key references to other data so I would like to work exclusively with my User model.
For example, when trying to make a Policy I am running into an error:
Argument 1 passed to App\Policies\ContractPolicy::before() must be an
instance of App\Models\User, instance of Illuminate\Auth\GenericUser
given
And the code simply looks like this:
<?php
namespace App\Policies;
use App\Models\Contract;
use App\Models\User;
use Illuminate\Auth\Access\HandlesAuthorization;
class ContractPolicy
{
use HandlesAuthorization;
public function before(User $user, $ability)
{
if ($user->isSuperAdmin()) {
return true;
}
}
public function viewAny(User $user)
{
return $user->isMember();
}
}
The GenericUser model is indeed returning the data from the users table, but the problem is that it is completely different from what my User model gives, and thus includes nothing like the foreign key references or functions I added. I was under the impression that I could switch the model used by switching the config/auth.php provider. Which I did and nothing seemed to change:
'providers' => [
'users' => [
'model' => App\Models\User::class,
],
'users' => [
'driver' => 'database',
'table' => 'users',
],
],
What am I doing wrong here?
You are defining the users provider to use the 'database' driver:
'users' => [
'driver' => 'database',
'table' => 'users',
],
Adjust that to use the 'eloquent' driver instead:
'users' => [
'driver' => 'eloquent',
'model' => App\Models\User::class,
],
The 'database' driver uses Query Builder to interact with the database table and returns an object of GenericUser to represent the currently authenticated user.
The 'eloquent' driver uses an Eloquent Model to interact with the database and returns a Model instance. If you like relationships and all the other features of Eloquent then you should stick with using the 'eloquent' driver. Also, you have defined your Policy methods to be expecting a particular Model passed to its methods.
I have configured Sluggable behavior on my model as follows:
public function behaviors() {
return [
[
'class' => SluggableBehavior::className(),
'attribute' => 'title',
'ensureUnique' => true,
]
];
}
I need to do:
If the user fills a form field called "URL", this should be used instead of the automatic generated slug.
If user changes the title, they will mark a checkbox if they want the slug updated.
I have found that Sluggable Behaviour has an attribute "immutable" but I do not see a method to manipulate it.
Also I do not see a way to stop automatic generation if value is given.
Any ideas?
For such unusual requirements you should probably extend SluggableBehavior and overwrite getValue() and isNewSlugNeeded() methods to feat your needs.
You may also play with $value property and/or change some behavior settings in beforeValidate() of model:
public function beforeValidate() {
$this->getBahavior('my-behavior-name')->immutable = !$this->changeSlugCheckbox;
return parent::beforeValidate();
}
But custom behavior is much more clean solution.
I´ve developed a package stored in LaravelRoot/packages/myname/project/ in Laravel.
Inside my package i´ll have to use an extended user-model containing a relationship not mentioned in the default usermodel.
Now here is my question:
How can i override the default User-Model inside my package?
More details:
If i receive the current user via Auth::user() inside my package i´ll receive an object using the default App\User.
I have extended this model containing now a new relationfunction, stored in LaravelRoot/packages/myname/project/src/App/Models/User (namespace: Myname\Myproject\App\Models).
Here´s my relation-function:
/**
* The roles that belong to the user.
*
* #return \Illuminate\Database\Eloquent\Relations\BelongsToMany
*/
public function roles()
{
return $this->belongsToMany(Role::class)->withTimestamps();
}
This relation is only used in my package, so i don´t want to store this function inside the default App\User. Is it possible to receive an object of my extended usermodel using Auth::user() in my package or do i have to receive this manually each time i need this one?
I think, the easiest way is to make your package model via extending of App\User model. You need just to open config/auth.php and to setup your model class.
'providers' => [
'users' => [
'driver' => 'eloquent',
'model' => Project\Models\User::class,
],
You will get correct model via Auth::user().
I've been developing web apps using Yii 1.1.14 so far, but now it's time for an upgrade.
The company where I work has developed its own Access Control system, and I was really OK with it until I saw what it was really like... A combination of 8 tables in the database (not counting the users table), with a bunch of foreign keys.
1 table for controllers
1 table for the actions
1 table for the menu categories
1 table for types of users
And the other tables basically just connect 2 or 3 of those tables at a time.
It works well, but in my point of view it's highly time consuming to maintain all those tables, and at some point, when your application goes online, if it hits a certain amount of users it could get really slow. specially because 2 of those tables have the user's table primary key as foreign key.
So I've decided that, when I start developing on Yii 2, I'm going to start using RBAC, so I started looking for tutorials online... Only finding many different versions of the same code with author's role, and permissions for create or update posts.
I found a combination of 5 videos on Youtube, but they are about Yii 1 RBAC. They were helpful because I managed to understand most of RBAC's functionality, but I still have some doubts that I'll
enumerate below. And keep in mind that for this Access Control system I'm using the DBManager class.
My Doubts
Yii 1's RBAC used to have 3 tables: auth_assignment, auth_item and auth_item_child. Now in Yii 2 RBAC, a new table appears that is called auth_rule and I still don't understand what that specific table is doing there, how to use it or how to populate it.
I see that it's possible to restrict the user's access to some actions by using the controller's behavior method, and assigning access to some actions depending on the user's role, but when it comes to this I have to split my question into 2:
2.1. First: If you can just restrict the access to actions by setting it up in the behaviors method, then what's the use of saving permissions to the auth_item table?
2.2. Second: If you DO decide to control access according to permissions, then how exactly do you do it, because I find myself writing the following type of code inside of every function and I don't think using RBAC is supposed to be this tedious. There has to be another way.
public function actionView($id)
{
if(Yii::$app->user->can('view-users')){
return $this->render('view', [
'model' => $this->findModel($id),
]);
}else{
#Redirect to a custom made action that will show a view
#with a custom error message
$this->redirect(['//site/notauthorized']);
}
}
Because of the Access Control System that we use right now, when a user logs in, a complex query is executed that will end up returning an array that will be saved as a session variable, and will be used to create a menu with as many dropdownlists as menu categories, that the controllers that the user has access to belong to. How can this be done with RBAC?
I can only really answer 2.2 of your question, as 3 doesn't sound at all like something an RBAC should do. You could, however, get the information you needed from the rules table most likely, provided you followed a naming convention that matched your controllers or actions.
On to answering 2.2 though:
You can simply set the behavior like such:
public function behaviors()
{
return [
'access' => [
'class' => AccessControl::className(),
'rules' => [
[
'allow' => true,
'actions' => ['view'],
'roles' => ['view-users'], //<-- Note, rule instead of role
],
]
]
}
This doesn't solve a different problem of 'view-own-users' style permissions, as this needs to inspect the ActiveRecord model (well, at least it does in my application). If You want to achieve this, take a look at my post in the Yii forums here:
http://www.yiiframework.com/forum/index.php/topic/60439-yii2-rbac-permissions-in-controller-behaviors/#entry269913
I use it in one of the simplest method,I use them in the behaviours of my controller.
public function behaviors()
{
return [
'access' => [
'class' => \yii\filters\AccessControl::className(),
'rules' => [
[
'allow' => true,
'roles' => ['sysadmin'],
'actions' => ['index','view','update'],
],
[
'allow' => true,
'roles' => ['staff'],
'actions' => ['index','create','update','view'],
],
],
],
];
}
Here roles are the one created in the auth-item table in the database and they have been assigned for users in auth-assignment table. In the behaviours we just use it as above. In the above code sysadmin can have access to index, view and update action, whereas staff can have access to index,create, update and view action.
Yii2 needs a little setup when it comes to using RBAC under your controllers AccessControl. I got around it by making my own AccessRule file.
namespace app\components;
use Yii;
class AccessRule extends \yii\filters\AccessRule
{
protected function matchRole($user)
{
if (empty($this->roles)) {
return true;
}
foreach ($this->roles as $role) {
if(Yii::$app->authManager->checkAccess($user->identity->code, $role))
return true;
}
return false;
}
then in your controller u can use something like this:
public function behaviors()
{
return [
'access' => [
'class' => AccessControl::className(),
'ruleConfig' => [
'class' => 'app\components\AccessRule'
],
'rules' => [
[
'actions' => ['index', 'resource-type'],
'allow'=> true,
'roles' => ['admin'],
],
],
],
];
}
Where admin is defined as a auth_item and the user is in the auth_item_assignments.
As I have created a new Rbac system for yii2. you can direct permission for a action and action will show you are not authorisez for this action.
By this you find that you will only provide access for action that need to identify.
I uploaded my detail here you can find lot of solution here.
This is the best solution i could come up with when facing the need to filter access by permissions, it's bothersome but can be useful if you're trying to create roles in a productive enviroment and want to use rbac.
use yii\web\ForbiddenHttpException;
if(Yii::$app->user->can('view-users')){
return $this->render('view', [
'model' => $this->findModel($id),
]);
}else{
throw new ForbiddenHttpException('You dont have access to this site');
}