Remove global scope on specific query - laravel

I've a User and Post model. Both of them has a Global scope to get only published or unblocked user.
Now, I need to get the list of all posts even if the author is blocked.
This is the query that I used.
$posts = Post::withoutGlobalScopes()->paginate(10);
But since I've the author relationship with it appended, I get the author as null (due to the global scope if the user is blocked).
How can I remove the global scope of the author in the above query and get all posts (published/unpublished) along with the author (blocked/unblocked).
How can I do this?

I had a similar case and I changed the definition of single relationships (especially ->belongsTo) to explicitly turn off the usually-global scope. If I had a reference to a Post and asked for its Author, I always wanted to receive the Author object, even if the Author was blocked.
class Post extends Model{
public function author(){
return $this->belongsTo('User')->withBlocked();
}
}
But in my case I didn't do the reverse for one-to-many relationships (if I had a reference to an Author, and asked for Posts I usually didn't mean unpublished, and I'd ask for them if I wanted them).

Related

In laravel, can I use accessor in controller or is there a better way to do it what I want to happen?

Context:
So let's say I have 3 tables: Genres, Books, Authors
and Each Book has a Genre and Author(s).
What I want to happen is return a list of Genres with all the related Books with Authors. Basically, group Books by Genre and include book authors. This is what I'm doing.
Genre->with(['books' => function($q) { $query->with('authors'); }]);
Problem I'm having is the speed since I have a lot of books and double that number is the number of authors. The speed is okay when Authors is not included and adds around 4sec when included which is not good since I just need the name of the Author - sometimes just one Author when the book has multiple author.
I'm thinking of creating an accessor in Books model to get just get the name of one Author related to it. I'm still figuring out how to do it and how I can append that accessor in controller because I don't want to use the protected $appends since it is not always needed.
Do you think it is better to use accessor or is there a better way to do what I wanted to happen?
You could adjust authors relationship to always return just one author (modify orderBy clause as needed).
$genre = Genre->with(['books.authors' => function($query) {
$query->orderBy('name')->limit(1);
}])->first();
And when fetching the author, you can assume that first entry is the one you need.
$genre->books->each(function (Book $book) {
$book->title;
$book->authors->first()->name;
});
To increase performance, make sure that your foreign keys ar indexed.

How to get modified relationship laravel

We have the models: Question and Answer.
In the answers table, we have a boolean column with the name accepted and we have just one accepted Answer. We want to get question with its answers and accept answer separately.
I use two approaches, but I don't know which is better:
1). check if the relation is loaded if loaded filter relations and get an accepted answer like this:
if(!$this->relationLoaded('answers')){
$this->load('answers');
}
return $this->answers->firstWhere('accepted',true);
2). We can use this approach too:
function accepted_answer(){
return $this->answer()->where('accepted',true);
}
The problem of the second approach is we get an array, but we expect a single response or null.
Is there any better approach to this or we can use neither 1 or 2?
Create an Accessor on Question model.
class Question extends Model
{
public function getSelectedAnswerAttribute()
{
return $this->answers->where('accepted', true)->first();
}
}
now in every Answer instance has an attribute called selected_answer.
You can access it like,
$answer->selected_answer;
Note
this 'selected_answer' is not fetching from database, it filters from already fetched answers list. so its more efficient than your 2nd approach.
and, If you want this attribute automatically bind to the Question model always. add this accessor to appends array.
Model
protected $appends = ['selected_answer'];
create accepted scope at your answer model.
public function scopeAccepted($query){
return $query->where("accepted",true)->first();
}
then you can easly retreive first accepted answer.
$question->answer()->accepted();
for more further check out query scopes;
You should go with the second approach as you will have a single source to change later on if something changes (instead of digging through a lot of controllers)
To get the result, use ->get(); at the end of builder query.
P.S: Scopes are generally always better than writing queries manually inside the controller

scopes should be in controller or model?

I am new to ruby and rails development and have this question in mind. If i have a concern with scope created say latest_records which gives me latest data for a customer
Now what is the best practice to use scope in this situation. should scopes be in model or in controller?
I read some online articles and it talks about fat model and skinny controller and since scopes are do database related work then i am guessing they should be in model.
Any suggestions or thoughts?
You guessed it right.
Scopes belong to model and needs active record classs object to work on.
Scopes are nothing but a activerecord query divided in parts and it helps look your query elegant and dry.
e.g.
If you want to get users with confirmed emails, you would:
User.where(confirmed: true)
But with scopes in your user model:
scope :confirmed, -> { where(confirmed: true) }
And you would simply:
User.confirmed
For more detailed please refer this answer here
A scope could only be defined in a model so it would have to be on a model.

Spring Data-Rest POST to sub-resource

Lets say I have the following structure:
#Entity
class Person extends AbstractPersistable<Long> {
String name
String surname
}
#Entity
class Task extends AbstractPersistable<Long> {
String description
#ManyToOne
Person person
}
If I follow proper HAL guidelines I'm not supposed to expose entity id's. Since I don't have a bi-directional relationship I cant PUT or PATCH to http://localhost:8080/persons.
Even if I did create the relation, I probably wouldn't want to first POST the Task to /tasks and then PUT to /persons, (mobile clients are going to kill me). But even then I don't have the Task ID even from the returned Entity so I can PUT to the Person entity. (I obviously can string parse but I don't think it's appropriate).
I probably wouldnt want to have a list of 1000 tasks in the Person entity either. So not exporting the Task entity is not really an option (and this means PATCH will not work)
So how am I supposed to associate the Person with the Task if I cannot get his id? What is the correct approach?
If you want to associate a Task with a Person you need the link to the person.
Lets say the person URI is http://localhost/persons/1
Then you could assign the person to a task by just passing that URI in the person attribute.
So a post to Task could look like this:
{
"description": "some text",
"person": "http://localhost/persons/1"
}
Spring-data-rest will lookup the person and take care of the rest.
In HAL Links are used to reference related resources not ids and a HAL entity should always return a Link to itself, which serves as its unique identifier.
If I'm not mistaken you can also annotate fields with DBRef and links should be generated for you.
If you want related resources to actually show up inline with data you'll have to draft a Projection. See more here:
Spring Boot REST Resource not showing linked objects (sets)
Last but not least - if you want Projections to also contain Links you'll have to make ResourceProcessor's for them, see here:
How to add links to Spring Data REST projections?

Manually inject model relation using Eloquent

How can I add a model into the relations array of another model?
E.g.
Domain belongsTo Owner.
Owner hasOne Domain.
I have $domain (instance of Domain).
I have $owner (instance of Owner).
I want to add $domain to $owner->relations[] so that I can just use $owner->domain later on in my code.
The reason for doing this is such that in one particular controller i only need a partial data set from each model so use fluent to query with a join for performance reasons then fill the models.
Then for readability's sake I'd like to use $owner->domain->id etc
$domain->owner()->associate($owner); gives me a $domain->owner option
But then I can't work out the opposite version
$owner->domain()->associate($domain)
$owner->domain()->attach($domain)
both result in the following fatal error
Call to undefined method Illuminate\Database\Query\Builder::[attach|associate] ()
NB: I don't want to save anything as I've already loaded all the data i need.
setRelation() should work. It sets the value in the relations array.
$owner->setRelation('domain', $domain);
When setting a one to many relationship, you may need to use values():
$owner->setRelation('domains', $domains->values());

Resources