How to get all roles and associated permissions to them - eloquent

I am trying to get all the roles whether they were assigned some permissions or not, if were assigned permissions then want to show them in front of each role using eloquent.
The Query I am trying
$roles = role::select('roles.*','role_permissions.permission_id')
->leftjoin('role_permissions', 'roles.id', '=', 'role_permissions.role_id')
->get();
Problem with this query is that, it repeats the role if multiple permissions were assigned to role.
for example:
It is showing result in this order (this is just for quick understanding).
How can I have result this way ['role_id' => 1, 'permission_id' => [1,2] ] with eloquent. Role model
public function permissions()
{
return $this->belongsToMany(\App\Models\permission::class, 'role_permissions');
}
Permission model
public function roles()
{
return $this->belongsToMany(\App\Models\Role::class, 'role_permissions');
}

You can use collection methods:
Role::with('role_permissions')->all()->map(function(Role $role){
return $role->permission_ids = $role->permissions->pluck('id');
})
Or you can use custom getter in Role model:
public function getPermissionIdsAttribute()
{
return $this->permissions->pluck('id');
}
$role->permission_ids;

Related

How do I achieve this security logic in Firebase FireStore?

The illustration below shows the logic of security rules that I need to apply for my firebase firestore.
Description
My app allows user authentication, and I manually activate client accounts. Upon activation, they can add users to their database collection, as well as perform operations on their database. I have illustrated each client as 'Organization' below, and each has multiple users which should only be able to access specific parts of the database collections/documents.
Each organization has an admin user who has full access over the database collection of that particular organization. Each user (from each organization) can access the node 'Elements', as well as their own UID-generated docs and collections only.
It seems like I need custom claim auths to achieve that. I wonder if some alternatives exist, like adding some specific security rules in my fireStore to make this work, or any other alternatives besides the firebase admin sdk tools, as it's time consuming and I'm not an expert backend developer.
Other Details
I use flutter. My app allows clients to authenticate and create their database collection. Clients add users (as team members) with different roles (which affect what collection/document they can access)
This security rules logic is the main thing I'm stuck on right now.
I highly appreciate suggestions and examples that might shed light on my way of achieving that.
illustration
My FireStore security rules right now
One possible solution:
Each organisation should contain a list of strings (userIds), and only users with userId in this list can access the Organisation collection and docs.
Database structure:
organisation_1:
userIds (field containing list of user ids - []<String>):
adminId (field containing admin id - String):
admin (collection):
users (collection):
elements (collection):
premium (collection):
organisation_2:
Security rules
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
function isLoggedIn() {
// only true if user is logged in
return request.auth != null;
}
match /organisation/{organisationId} {
function prefix() {
return /databases/$(database)/documents/organisation/$(organisationId);
}
function isAdmin() {
// only true if admin
return isLoggedIn() && request.auth.uid == get(/$(prefix())).data.adminId;
}
function isUser() {
// only true if user
return isLoggedIn() && request.auth.uid in get(/$(prefix())).data.usersId;
}
function isDataOwner(dataId) {
// only true if user is admin or userId is the document id.
// this rule should allow each user access to their own UID-
// generated docs and collections only
return isLoggedIn() && (isAdmin() || dataId == request.auth.uid);
}
// since userIds list is organisation data, we should prevent any
// user from editing it (or only allow admin to edit it).
// if you are using cloud function to update userIds list, set this
// to false. Cloud function does not need access.
allow write: if isAdmin();
allow read: if true;
match /Elements/{elementsId=**} {
// allow access to the entire Elements collection and
// subcollections if isAdmin or isUser.
allow read, write: if isAdmin() || isUser();
}
match /settings/{userId} {
// allow access only if document id is your userId
allow read, write: if isDataOwner(userId);
}
match /adminDocs/{docId} {
// only allow admin
allow read, write: if isAdmin();
}
}
}
}
Then you can use a cloud function to keep your userIds list up to date. Example:
const functions = require("firebase-functions");
const admin = require("firebase-admin");
const db = admin.firestore();
exports.onCreate = functions.firestore
.document("/organisation/{organisationId}/users/{userId}")
.onCreate((_, context) => {
const params = context.params;
const organisationId = params.organisationId;
const userId = params.userId;
const data = {
userIds: admin.firestore.FieldValue.arrayUnion(userId),
};
return db.doc(`/organisation/${organisationId}`)
.set(data, { merge: true });
});
exports.onDelete = functions.firestore
.document("/organisation/{organisationId}/users/{userId}")
onDelete((_, context) => {
const params = context.params;
const organisationId = params.organisationId;
const userId = params.userId;
const data = {
userIds: admin.firestore.FieldValue.arrayRemove(userId),
};
return db.doc(`/organisation/${organisationId}`)
.set(data, { merge: true });
});
You can avoid this cloud function by simply adding userid to userId list when admin creates new user. But cloud function is cleaner (Use it).
UPDATE
$(database) is the name of your firestore database.
{database} (line 3 in my security rules) tells rules to save the actual name of database into database variable.
prefix() returns the path to the organisation document.
If a user tries to access his document in this path organisation/12345/users/67890, then $(database) is default and prefix() returns /databases/default/documents/organisation/12345/
You can go to firestore docs to see how $(database) and path (prefix()) is being used.

Balancing between filter and getting the name of foreign key in eloquent

I have a controller that will list all items like this:
$users = User::all();
return view('auth.userslist', compact('users'));
And In my Users Model, function that will display the name of foreign key items:
public function role() {
return $this->belongsTo('App\Models\Roles');
}
Displayed in blade like this:
<td>{{$user->name}}</td>
<td>{{$user->role->role_name}}</td>
Which works fine. Now, I want to add a filter for my user list(Active and Inactive). Based on docs, I need to do it like this:
$users = DB::table('users')
->where('user_delete', '=', 0)
->get();
return view('auth.userslist', ['users' => $users]);
But it will return an error message Undefined property: stdClass::$role. If I remove all the foreign key fields, it will filter just fine.
Use with() or join
$users = User::with('role')->where('user_delete', '=', 0)
->get();

Yii2: How to do a simple join query?

I am learning how to do simple queries using the Yii2 framework. I use PostgreSQL.
I am trying to join two tables and get the data from both tables with a where condition.
The tables are called Admins and Persons.
The join use field called idadm.
The condition is idadm = 33. This works great but the result has data only from the Admins table and I need data from the other table.
Here is my example:
$query = \app\models\Admins::find()
->select('*')
->leftJoin('persons', 'persons.idadm = admins.idadm')
->where(['admins.idadm' => 33])
->with('persons')
->all();
I am following the Yii2 official guide: http://www.yiiframework.com/doc-2.0/guide-db-active-record.html
Update: Here I show the updated code that doesn't solve de problem:
You need to write all column name in select().
$query = \app\models\Admins::find()
->select('admin.*,persons.*') // make sure same column name not there in both table
->leftJoin('persons', 'persons.idadm = admins.idadm')
->where(['admins.idadm' => 33])
->with('persons')
->all();
And also you need to define person table attributes in Admin model.
Second way is get records as array,so you dont need to define attributes in Admin model.
$query = \app\models\Admins::find()
->select('admin.*,persons.*') // make sure same column name not there in both table
->leftJoin('persons', 'persons.idadm = admins.idadm')
->where(['admins.idadm' => 33])
->with('persons')
->asArray()
->all();
Ensure that active record has required relations, e.g. something like follows:
class Admins extends \yii\db\ActiveRecord {
public function table() {
return "admins";
}
public function getPersons()
{
return $this->hasMany(Person::className(), ['idadm' => 'idadm']);
}
}
class Person extends \yii\db\ActiveRecord {
public function table() {
return "persons";
}
}
Then use joinWith to build query:
$query = Admins::find()
->joinWith('persons')
->limit(1);
$result = $query->createCommand()->getSql();
echo $result;
Here is produced query:
SELECT `admins`.* FROM `admins`
LEFT JOIN `person` ON `admins`.`idadm` = `person`.`idadm` LIMIT 1

How to update only one column value in database using Entity Framework?

I have a table with columns username, password. Data can be updated by admin for the table.
Now I want to add a new column named IsAgree (as a flag), it need to set when user logs in for the first time. But while setting data, it should not affect other column data, I will only send usename and Isagree flag as an object.
flgDetail = { usename:"vis" Isallowed:True }
[Route("api/TermsAndCondition/setflag")]
public IHttpActionResult post(FlagDetails FlagDetail)
{
var user = _context.table.Where(s => s.UserName == flagDetail.UserName);
}
Should I use post or put?
How can I update only one column?
And it should not affect other columns.
you can use either post or put.It's not a problem.You can update it as shown below.
[Route("api/TermsAndCondition/setflag")]
public IHttpActionResult post(FlagDetails flagDetail)
{
var user = _context.table.Where(s => s.UserName == flagDetail.UserName);
user.IsAgree =flagDetail.Isallowed;
_context.SaveChanges()
}

How to get specific fields or update theme and get the relation fildes from api calls in yii2 REST API?

I'm trying default yii2 api rest calls GET to get some model fields or PUT to update some model fields but I can't find a way to do this. I can only get all the fields or update theme all. Any help to do this? And how can I get the related relational field to this model?
I'm trying like this like
GET localhost/my-website-name/api/web/v1/vendors/
PUT localhost/my-website-name/api/web/v1/vendors/1
one way that I know for customizing fields is overriding fields function in your model like this
public function fields() {
return [
'id',
'iso3' => function() {
return base64_encode($this->iso3);
}
];
}
How to get specific fields and get the relation fields from api calls?
By default, yii\db\ActiveRecord::fields() returns all model attributes which have been populated from DB as fields, while yii\db\ActiveRecord::extraFields() should return the names of model's relations.
Take this model for example:
class Image extends ActiveRecord
{
public function attributeLabels()
{
return [
'id' => 'ID',
'owner_id' => 'Owner ID',
'name' => 'Name',
'url' => 'Url',
'created_at' => 'Created At',
'updated_at' => 'Updated At',
];
}
public function getOwner()
{
return $this->hasOne(Owner::className(), ['id' => 'owner_id']);
}
public function extraFields()
{
return ['owner'];
}
}
Here I did override the extraFields() method to define the owner relationship. Now if I want to retreive all images but selecting id and name fields only and each resource should also hold its related owner data I would simply request this url:
GET example.com/images?fields=id,name&expand=owner
note: you can also use comma separation to expand more than one relation
In case you want to permanently remove some fields like created_at and updated_at you can also override the fields() method:
public function fields()
{
$fields = parent::fields();
unset($fields['created_at'], $fields['updated_at'], $fields['owner_id']);
return $fields;
/*
// or could also be:
return ['id', 'name','url'];
*/
}
this way the following request should only return image's id, name and url fields along with their related owner :
GET example.com/images?expand=owner
If owner's fields should be filtered too then override its fields() method too in its related class or a child class of it that you tie to the image model by using it when defining the relation.
See official documentation for further details.
PUT to update some model fields
Yii only updates dirty attributes. so when doing:
PUT example.com/images/1 {"name": "abc"}
the generated SQL query should only update the name column of id=1 inside database.
Hello i manage to find a solution for this situations
first get some model fields only and with related entities fildes:
public function fields() {
return [
'name',
'phone_number',
'minimum_order_amount',
'time_order_open',
'time_order_close',
'delivery_fee',
'halal',
'featured',
'disable_ordering',
'delivery_duration',
'working_hours',
'longitude',
'latitude',
'image',
'owner' => function() {
$owner = Owners::findOne($this->owner_id);
return array($owner);
}
];
}
if you want to remove some fildes
public function fields()
{
$fields=parent::fields();
unset($fields['id']);
return $fields;
}
update only specific fields
public function beforeSave($insert)
{
$lockedValues = ['name', 'halal', 'featured', 'latitude', 'longitude', 'image', 'status', 'created_at', 'updated_at'];
foreach ($lockedValues as $lockedValue) {
if ($this->attributes[$lockedValue] != $this->oldAttributes[$lockedValue])
throw new ForbiddenHttpException($lockedValue . " can't be changed.");
}
return parent::beforeSave($insert); // TODO: Change the autogenerated stub
}