How to hide columns when using "with" in Laravel? - laravel

How would I hide unwanted data for a cleaner response in laravel when using with statements.
For example, let's pretend I have a single post. That post has comments, and then comments have multiple tags related to each comment.
So I have below to illustrate the idea.
I have a model called Post
I then have a relationship in Post model that has comments relationship, ie
public function PostComments()
{
return $this->hasMany('App\Models\Post\Comments', 'postId', 'id');
}
I also have a model called PostComments
I then have this relationship in there.
public function PostCommentsTags()
{
return $this->hasMany('App\Models\Posts\PostCommentTags', 'postCommentId', 'id');
}
Then in my controller I have
$post = Post::with(
'PostComments',
'PostComments.PostCommentsTags',
)->first();
This works wonderfully, I get a response like this.
{
title: 'This is a title',
description: 'Description',
comments:[
{
id: 1,
comment: 'this is a comment',
commentTags:[{
id: 1,
tagName: 'emotion',
tagValue: 'angry',
},
{
id: 10,
tagName: 'timeOfDay',
tagValue: 'morning',
}],
},
{
id: 2,
comment: 'this is a comment too',
commentTags:[{
id: 7,
tagName: 'emotion',
tagValue: 'happy',
},
{
id: 9,
tagName: 'timeOfDay',
tagValue: 'evening',
}],
},
{
id: 3,
comment: 'Too many comments now',
commentTags:[{
id: 12,
tagName: 'emotion',
tagValue: 'angry',
},
{
id: 14,
tagName: 'timeOfDay',
tagValue: 'evning',
}],
}
]
}
But I want to get rid of a lot of clutter, ie id. (the real thing is a lot more complex than this example, so has a lot of clutter and unneeded info)
So I have this below, which hides all the ids for comments, this works great!
$post->Comments->makeHidden(["id"]);
But how do I hide it for Comment tags too? (I want no ids in my json)
I have tried below, but it does not work? How do you access nested models when using the with statement. I can't seem to find anything to help.
$post->Comments->CommentsTags->makeHidden(["id"]);
Spent a bit to much time on this now so time to ask for help. Please help :)
Best wishes.

Argghh finally figured it out. If you want to remove unwanted data, you loop through it like this.
foreach($post->Comments as $comment){
$comment->CommentsTags->makeHidden(['id','created_at','updated_at']);
}
I guess its a nice way to have hidden attributes without having it the model.

Related

How to use "where" clause nested n relationships?

There are lot available reference that I went through before asking the question. And it seems all of them were just one to two layer relationship only. While my case is different because it can have n layer of relationship, so it's not really helpful in my end.
I have here nested relationship of Person.
class Person extends Model
{
public function children()
{
return $this->hasMany(Person::class, 'parent_id', 'id')->with('children');
}
}
This would result to this sample Model structure data. (show snippet below)
[
{
name: 'Don Pepe',
gender: 'male',
children: [
{
name: 'Juan',
gender: 'male',
children: [
{
name: 'Vito',
gender: 'male',
children: ... and so on, so forth
},
...
],
},
...
],
},
...
]
I would like to ask how to filter these data. For example like, if I would like to filter the male gender? We could just simply where clause the parent but for children of children, how we gonna where clause it?
I doubt it could not be done in Laravel Eloquent, only in collection. Please let me know your thoughts.
Laravel nested relationships
https://laracasts.com/discuss/channels/laravel/eloquent-nested-relations-with-where-clause

Graphql: How can I solve the N + N problem?

After having implemented dataloader in the respective resolvers to solve the N+1 problem, I also need to be able to solve the N+N problem.
I need a decently efficient data loading mechanism to get a relation like this:
{
persons (active: true) {
id,
given_name,
projects (active: true) {
id,
title,
}
}
}
I've created a naive implementation for this, returning
{
persons: [
{
id: 1,
given_name: 'Mike'
projects: [
{
id: 1,
title: 'API'
},
{
id: 2,
title: 'Frontend'
}
]
}
{
id: 2,
given_name: 'Eddie'
projects: [
{
id: 2,
title: 'Frontend'
},
{
id: 3,
title: 'Testing'
}
]
}
]
}
In SQL the underlying structure would be represented by a many many to many relationship.
Is there a similiar tool like dataloader for solving this or can this maybe even be solved with dataloader itself?
The expectation with GraphQL is that the trip to the database is generally the fastest thing you can do, so you just add a resolver to Person.projects that makes a call to the database. You can still use dataLoaders for that.
const resolvers = {
Query: {
persons(parent, args, context) {
// 1st call to database
return someUsersService.list()
},
},
Person: {
projects(parent, args, context) {
// this should be a dataLoader behind the scenes.
// Makes second call to database
return projectsService.loadByUserId(parent.id)
}
}
}
Just remember that now your dataLoader is expecting to return an Array of objects in each slot instead of a single object.

Laravel 5.5 - Merge 2 collections into one based on id?

So I have 2 models Books and Classes:
$books = Books::limit(3)->get(['id','classable_id','easy_book']);
// Books returned:
{ id: 200,
classable_id: 2,
easy_book: false
},
{ id: 201,
classable_id: 3,
easy_book: true
},
{ id: 202,
classable_id: 4,
easy_book: false
}
$classIds = $books->pluck('classable_id');
$classes = Classes::whereIn('id', $classIds);
// Classes returned:
{ id: 2,
subject: Math,
students: 30
},
{ id: 3,
subject: History,
students: 30
},
{ id: 4,
subject: Physics,
students: 30
}
Then trying to get the following output (without combining the queries, but keeping them separate like above, and just using php logic to output):
Classes returned:
{ id: 2,
subject: Math,
students: 30.
easy_book: false }, // trying to merge this!
{ id: 3,
subject: History,
students: 30.
easy_book: true}, // trying to merge this!
{ id: 4,
subject: Physics,
students: 30.
easy_book: false } // trying to merge this!
Basically, I am trying to merge the easy_book field from books returned to the respective class returned based on class.id == books.classable_id. Any idea how to merge it?
Add a relationship to your Books model like so:
public function class() {
return $this->belongsTo(Classes::class, 'id', 'classable_id);
}
Then you can do:
Book::with('class')->select('id', 'classable_id', 'easy_book')->limit(3)->get();
Each collection item will then have a collection of classes where applicable.
If after that you want to manipulate them, you can use the map function as documented here: https://laravel.com/docs/5.7/collections#method-map

bootstrap-typeahead's displayfield using multiple values

I'm sorry if this is a duplicate question but I do not understand the answers of other people. I'm using Twitter Bootstrap Ajax Typeahead Plugin (https://github.com/biggora/bootstrap-ajax-typeahead/) to search emails from data that comes from an SQL query. This is the code I use with a php file, where I use people's emails as valueField and people's names as displayField and it works well.
inputSearch.typeahead({
ajax: {
url: urlAjax + '?requete=rechercheannuaire',
displayField: "description",
valueField: "id",
triggerLength: 2,
method: "get",
loadingClass: "loading-circle",
preProcess: function(data){
if(data.type === "error")
{
return false;
}
return data.datas;
}
},
onSelect: function(data){
//alert("assez tot");
data.text = data.value;
//console.log(data);
$("#chercherinvite").val(data.text);
return data;
}
});
The problem is that I have to be able to search "Dujardin" as well as "Du Jardin" and I cannot find a way to assign multiple values to displayField. If someone could explain how typeahead works, I'd be thankfull, I don't understand the documentation.
According to the plugin documentation, you cannot assign multiple values to the displayField option. However, it is possible for you to re-write events.
After a quick lookup into the source code of bootstrap-ajax-typeahead, we can figure out that the "matcher" event is used as the filter for displaying - or not - values to the user.
To allow to match both "Du jardin" and "Dujardin", we have to manipulate strings. Here, I suggest you to :
Remove any diacritic character
Remove any non-word character (all except [A-Za-z0-9_])
Remove any underscore
Set the string to lowercase
To do #1, I suggest you to use this fantastic script by rdllopes.
I wrote a POC. Here is the JSON source (called "source.json"):
[
{ "id": 1, "name": "jdupont#example.com - Jean Du Pont"},
{ "id": 2, "name": "jdupont2#example.com - Jean Dupont"},
{ "id": 3, "name": "jdupont3#example.com - Jéan Dupônt"},
{ "id": 4, "name": "mbridge#example.com - Michel Bridge"}
]
And here is the script that I used for matching elements :
$('#search').typeahead({
// Our source is a simple JSON file
ajax: 'source.json',
// Display field is a list of names
displayField: 'name',
// And value field a list of IDs
valueField: 'id',
matcher: function(item)
{
// For both needle and haystack, we :
// 1. Remove any diacritic character
// 2. Remove any non-word character (all except [A-Za-z0-9_])
// 3. Remove any underscore
// 4. Set the string to lowercase
var needle = removeDiacritics(this.query).replace(/[^\w]/gi, '').replace('_', '').toLowerCase();
var haystack = removeDiacritics(item).replace(/[^\w]/gi, '').replace('_', '').toLowerCase();
// Does the needle exists in haystack?
return ~haystack.indexOf(needle);
}
});

Laravel Add attribute to model in __constructor

I'm trying to add an attribute to my model so I can say something like: $model->path and then get back a url. So I've added the following to the model's constructor:
public function __construct($attributes = array()){
$this->path = url('img/' . $this->{'file-name'});
parent::__construct($attributes);
}
But if I run Model::first() then I get the following:
{
id: 25,
text: "A lovely file",
file-name: "file.jpg",
created_at: "2016-02-12 11:44:37",
updated_at: "2016-02-12 11:44:37"
},
You'll notice that there is no path attribute. Am I doing something very wrong?! I want to see:
{
id: 25,
text: "A lovely file",
file-name: "file.jpg",
path: "http://myapp.app:8000/img/file.jpg",
created_at: "2016-02-12 11:44:37",
updated_at: "2016-02-12 11:44:37"
},
For the record, I have also tried $this->field = 'value'; and that didn't create an attribute either.
Read the documentation. You’d be better off using an accessor method.
public function getPathAttribute()
{
return url('img/'.$this->file_name);
}
You also shouldn’t be using dashes in column/property names, use the convention of underscores for separators.

Resources