Laravel: Find specific pivot value from three-way pivot - laravel

I previously made this question: laravel: How to get column related to another column in pivot table (3 column pivot)
Which helped me loop through my three-way pivot and link each User, Account, and Role.
The implementation doesn't concern itself with a specific user or account.
On my app, a single Account can have many users. These users have specific roles on accounts like "owner", "admin", "manager", etc.
I'm creating a policy to determine if a user has permission based on their role on a specific account to perform an action.
I'm playing around with Tinker. So if I grab a user, and do $user->roles, it'll output
>>> $user->roles
=> Illuminate\Database\Eloquent\Collection {#3226
all: [
App\Role {#3250
id: 1,
name: "owner",
manage_billing: 1,
manage_users: 1,
close_account: 1,
created_at: "2020-05-02 12:34:39",
updated_at: "2020-05-02 12:34:39",
pivot: Illuminate\Database\Eloquent\Relations\Pivot {#3219
user_id: 1,
role_id: 1,
account_id: 1,
},
},
],
}
Given that I have an account_id, I'd like to check the role on that specific account.
I tried something like:
$user->roles->wherePivot('account_id', $account->id);
but the method wherePivot doesn't exist on collections.
(the $account variable is just $user->accounts()->first() in this example)
My Models and relationships are on the question I linked above.

This seems to be what I wanted:
$user->roles()->wherePivot('account_id', $account->id)->first();
My mistake was not doing roles()

Related

Loopback4 filter inside the scope return list still

I'm using lb4
I have some problems here, I try to find the list with where conditions inside the scope with pagination.
const teacherWithStudents = await this.teacherRepository.find({limit:10,skip:0,
include: [{
relation: "student",
scope: {
where: { "name": "some random name here" },
}
}]
})
The expected teacher's array is : [] (because I searched a random string in student name which is not in DB)
but I got teachers to array without student like this: [{teacherId:1,teacherName:"Stella"}{teacherId:2,teacherName:"Mery"}]
if I filter student names if no teacher has a student that I filtered I need an empty array but I get only a teacher.
I hope I explained the issue in detail.
Thanks in advance
This is expected as the parent and relation queries should be perceived as two separate queries.
First, the list of teachers based on the parent query are resolved. The IDs of the resolved teachers are then used as a constraint when querying for the list of students.
Both results are then combined together to create the final response.
Loopback uses left join. if you want to find only teachers where student is not null then you have to use inner join with native query.

Assign value to hash argument on condition

I have a ActiveRecord backend model where I am inserting records. However when I am assigning value to certain attributes i would like to assign them based on a certain condition being satisified or not. How can I go about doing it? Have attached an example below for better sake of clarity.
#user = User.find_by_name("John")
Store.create(
name: "Some Store",
email: "store#example.com",
user_id: #user.id if #user.applicant?
)
Yes, you can't apply suffix if to keyword arguments / hash elements this way. I normally do something along these lines:
store_params = {
name: "Some Store",
email: "store#example.com",
}
store_params[:user_id] = #user.id if #user.applicant?
Store.create(store_params)
This also works well if there's an existing user_id value that needs to be preserved if #user is not an applicant. For example, when you're updating records. For creation, simple parenthesizing should work, as pointed out by others
user_id: (#user.id if #user.applicant?)
Caveat: this assumes that the default value for user_id is nil, so a nil produced from the expression when user is not an applicant is equal to the value set when user_id was not supplied at all.
You can use safe-operator here
Just use brackets to avoid interpreter error
Store.create(
name: "Some Store",
email: "store#example.com",
user_id: (#user.id if #user&.applicant?)
)

i need to get a user id from a query result

am querying from my User table using the user name, how do i get the user id from the query object
User.where(user_name: "john")
my goal is to get the User id: 5, i thought it was as easy as (.id), but thats not working
=> #<ActiveRecord::Relation [#<User id: 5, user_name: "john", user_city: "India", created_at: "2019-01-19 18:02:32", updated_at: "2019-01-19 18:02:32">]>
Materialized where will return a collection, so you can get first record with .first
User.where(:user_name => "john").first&.id
Or use find_by which will return first record which satisfies condition.
User.find_by(:user_name => "john")&.id
If you're really only trying to get the id (i.e., not using any other attributes of the object) it's usually better to formulate this as a .pluck. This requires less time for ActiveRecord to instantiate, and makes the query job take better use of your database indices. As long as User#user_name is unique (and I'd hope so!) it will return an array of length 1.
User.where(user_name: "John").first.&id
# SELECT "users".* FROM "users" WHERE "users"."user_name" = "John"
# ORDER BY "users" ASC LIMIT 1
# => 1 or nil
User.where(user_name: "John").pluck(:id).first
# SELECT "users"."id" FROM "users" WHERE "users"."user_name" = "John"
# => 1 or nil
Unfortunately, find_by and its helpers don't work this way with pluck, and both of these statements instead result in errors
User.find_by(user_name: "John").pluck(:id)
User.find_by_user_name("John").pluck(:id)

group method for activerecord. Docs are confusing?

I don't get what this means in the Rails tutorial:
group(*args) public
Allows to specify a group attribute:
User.group(:name)
=> SELECT "users".* FROM "users" GROUP BY name
Returns an array with distinct records based on the group attribute:
User.group(:name) [User id: 3, name: "Foo", ...>, #User id: 2, name: "Oscar", ...>]
I don't see the grouping with the example they gave...
Group is most useful (I think) if you are trying to count stuff in your database or if you join multiple tables. Let me give a few examples.
1.
If you want to know how many users there are in your data base with each name then you can do:
User.group(:name).count
this will return a hash looking something like this:
{ ann: 4, bert: 15, cecilia: 3 ... }
I do not know why there are so many Berts in your database but anyway...
2.
If your users have related records (for instance cars) the you can use this to get the first car included in your activerecord model (the reason it will be the first is because of how group works and is further explained in the link below)
User.joins(:cars).select('users.*, cars.model as car_model, cars.name as car_name').group('users.id')
Now all records in this result will have a method called car_model and one called car_name.
You can count how many cars each user has with one single query.
User.joins(:cars).select('users.*, count(cars.id) as car_count').group('users.id')
Now all records will have a car_count.
For further reading: Mysql group tutorial
Hope this shed enough light over groups for you to try them out a little bit. I do not think you can fully understand them until you worked with them a little bit.

Finding Related Rows in Doctrine -- "Cleanest" Design?

I'm new to Doctrine, and I'm trying to get my head around both Doctrine and Symfony at the same time, so please bear with me.
I have a "Sentence" object. Sentences can be "rated" (scored out of five) by Users. I'm modelling this with the fairly obvious design where I have a Sentence table, a User table and a Rating table. Each row in the Rating table represents one user's rating of one sentence.
Rating:
...
columns:
rating: { type: integer } # This is my score out of five
sentence_id: { type: integer }
user_id: { type: integer }
relations:
sfGuardUser: { local: user_id }
Sentence: { local: sentence_id, onDelete: CASCADE, foreignAlias: Ratings }
That's working fine, but I'm struggling to find the cleanest way of asking questions of my model.
For example, in my code I have a logged-in User, related to my session, and I'm displaying a page representing a Sentence, which is also already sitting in a variable. I want to display the logged-in user's rating of that Sentence.
In raw SQL, effectively what I want to do is:
SELECT
rating
FROM
rating_table
WHERE
sentence_id = {$sentence->id} AND user_id = {$user->id}
Now, I know I could just do this on the RatingTable, simply by writing a new query with a bit of DQL, or even using one of the "magic" finders. But that seems a bit clumsy and not very OO, when I already have a Sentence object in memory with a getRatings() method generated by Doctrine for me which must already have the rating I want in its Collection of "all ratings for this sentence".
Is there an easy, efficient way that I'm missing of doing something like $sentence->getRatings()->findByUserId($user->getId())?
Or is it actually sensible just to ignore the fact that I've already got a collection of "ratings for this sentence" in memory and dive back out to the database, ignoring them completely?
There isnt a finder within a collection like that that im aware of (unlike with Propel)... if there is and I missed it in the Doctrine API then im going to leanr a blissful piece of code when someone posts it :-)
Having said that, IF you already have the collection loaded i would just add a custom method tot he model.. for example:
public function getUserRating($user){
if($user instanceof Doctrine_Record){ // or sfGuardUser depending on how tightly you want to couple
$user = $user->getId();
}
foreach($this->getRatings() as $rating){
if($user == $rating->getUserId()){
return $rating;
}
}
return null;
}

Resources