I am implementing an export function in an app I have built.
The export process fetches a collection of model values and then exports this into a spreadsheet that is downloaded to the client.
On my model, I have both a user_id and a responsibility. Both of these fields contain an ID that maps back to my users table (usually to two different users).
During the export process, I want to replace the user_id and responsibility values with the actual users' names, as opposed to their user IDs.
map() and transform() seem to be the right methods to achieve what I am after, however I can't see an elegant way to achieve what I want.
This is what I have so far:
$forms = Form::where('created_at', '>=', (new Carbon('first day of last month'))->toDateString())
->where('created_at', '<=', (new Carbon('last day of last month'))->toDateString())
->get();
$forms->transform(function($form) {
return [
'responsibility' => is_null($form->responsibility) ? $form->responsibility : $form->personResponsible->name,
'user_id' => $form->user->name
];
});
However, this only returns responsibility and user_id in the collection - as opposed to just updating these values.
Now, I could just include the rest of the columns in the return array, but that seems unnecessary - surely there is a better way to do this.
How can I replace two of the values in my model collection with the users' names most efficiently?
Just modify the valules you want to transform and return the $form Model:
$forms->transform(function($form) {
$form->responsibility = is_null($form->responsibility) ? $form->responsibility : $form->personResponsible->name;
$form->user_id = $form->user->name;
return $form;
});
Related
I have a collection which is an eloquent query.
There is one column where I want to replace the value with another value.
I am using the transform function to do this however it is not working as intended.
Here is my query in the controller :
$articles = KnowledgeBaseArticle::getArticlesByDepartment($department)
->get()
->transform(function ($article) {
$article->category_id = KnowledgeBaseCategory::find($article->category_id)->name;
});
And the getArticlesByDepartment query from the model:
public function scopeGetArticlesByDepartment($query, $department){
return $query->where('department', $department)
->select('title', 'updated_at', 'department', 'id', 'category_id')
->orderBy('title', 'asc');
}
I want to return it so that all the rows with column category_id is replaced with the category name. You can see I am trying to do this by using $article->category_id by using find on the KnowledgeBaseCategory model to retrieve this. However this is not working at all and when I die and dump, I get an single column array full of nulls.
When I have died and dumped $article->category_id & find query inside the transform, it is returning the correct category name, it is just not replacing the category_id column with the category name.
I have also tried map instead of transform and got the same result.
If it matters, I am later on converting this data into JSON.
Where am I going wrong?
Transform, not unlike map, needs you to return the modified item of the collection, as this will replace the existing item.
transform(function ($article) {
$article->category_id = KnowledgeBaseCategory::find($article->category_id)->name;
return $article;
});
Since objects are mutable and passed by reference, you can just do this in an each() closure instead to save a line:
each(function ($article) {
$article->category_id = KnowledgeBaseCategory::find($article->category_id)->name;
});
Though, you really should have a relationship set up for category. There's no reason to be performing this find logic in your controller.
I want to ask about how to pull out the specific data (some columns) from the Laravel database
Here is my code :
User Model :
public function events(){
return $this->belongsToMany('App\Events')->withTimestamps();
}
Event Controller :
public function showjson(){
$user_id=Auth::user()->id;
$events = DB::table('events')
->select(
'id',
'calendar_title as title',
'startdate as start',
'enddate as end',
'calendar_color as backgroundColor',
'calendar_color as borderColor')
->where('user_id',$user_id)
->get();
$user=Auth::user();
$eventData=$user->events;
return $eventData;
}
I have already the relationship between this two, I can get the data through this :
$user = Auth::user();
$eventData = $user->events;
But I want to get specific columns by name, like in select code above.
Would that be another way that I can call the specific data and change the column name?
i want display in json only the calendar_title, calendar_des only.
You can use lists to retrieve a list of column values, take a look at Database Query Builder Retrieving Results section :
$user->events->lists('calendar_title','calendar_des');
If you want to return Json format you can use toJson() method :
$user->events->lists('calendar_title','calendar_des')->toJson();
Hope this helps.
I am wanting to limit a controller's function's result to only pass certain columns into the view.
It is necessary because it will be used within an API, and so I need the results to be as streamlined as possible.
I have done this successfully with the following function:
public function getIndex()
{
$alerts = Criteria::select('id', 'user_id', 'coordinate_id', 'alert_name')
->with(['coordinate' => function($q){
$q->select('name', 'id');
}])
->get();
}
So it only returns id, user_id and coordinate_id from the criteria table.
However on the function below, I am using a has query (to access a relationship), and thus, using with afterwards to limit the columns, but it's still returning all:
public function getMatches()
{
$matches = Criteria::select('id')
->has('alerts')
->with(['alerts' => function ($q){
$q->select('id', 'headline', 'price_value', 'price_type');
}])
->with('alerts.user.companies')
->get();
}
But, for example, it's still returning the description column, which is in the alert's table. The with query proceeding the has query clearly isn't working (but it's presenting no errors).
Also, the ->with('alerts.user.companies') query, is returning everything within the user's table, which is also unnecessary. How can I return just the companies table data, that's related to the user, who's related to the alert?
Your help would be greatly appreciated.
Depending what you want to achieve, you could use $hidden property to hide columns you don't want to return as json or arrays.
In your Alert model you could do:
protected $hidden = ['description'];
And this way description field won't be returned.
If it's not the way for you (sometimes you want to return description) you could create extra relationships where you limit fields from database.
You could for example create the following relationship:
public function alertsSimple() {
return $this->hasMany('Alert')->select('id', 'headline', 'price_value', 'price_type', 'criteria_id');
}
Also maybe in your select the problem is that you don't use foreign key at all. You could also try with:
$q->select('id', 'headline', 'price_value', 'price_type','criteria_id');
instead of
$q->select('id', 'headline', 'price_value', 'price_type');
I have two table for post and user. I want to show post count of user in users list gridview. In yii 1 I use this in model to define a relation for this purpose:
'postCount' => array(self::STAT, 'Post', 'author',
'condition' => 'status = ' . Post::ACTIVE),
...
User:find...().with('postCount').....
But i don't know how to implement this in Yii2 to get count of post in User:find():with('...') to show in gridview.
Anyone try this in yii2?
Here is an example of what I did and it seems to work fine so far. It was used to get a count of comments on a post. I simply used a standard active record count and created the relation with the where statement using $this->id and the primary key of the entry its getting a count for.
public function getComment_count()
{
return Comment::find()->where(['post' => $this->id])->count();
}
Just a passing it along...
You can try the code below:
User::find()->joinWith('posts',true,'RIGHT JOIN')->where(['user.id'=>'posts.id'])->count();
Or if you want to specify a user count:
//user id 2 for example
User::find()->joinWith('posts',true,'RIGHT JOIN')->where(['user.id'=>'posts.id','user.id'=>2])->count();
Please note that, posts is a relation defined in your User model like below:
public function getPosts()
{
return $this->hasMany(Post::className(), ['user_id' => 'id']);
}
Well still I think for those who it may concern, if you JUST want the count a select and not the data it will be better use this instead imho:
$count = (new \yii\db\Query())
->select('count(*)')
->from('table')
->where(['condition'=>'value'])
->scalar();
echo $count;
Of course I can use order_by with columns in my first table but not with columns on second table because results are partial.
If I use 'join' everything works perfect but I need to achieve this in eloquent. Am I doing something wrong?
This is an example:
//with join
$data = DB::table('odt')
->join('hdt', 'odt.id', '=', 'hdt.odt_id')
->order_by('hdt.servicio')
->get(array('odt.odt as odt','hdt.servicio as servicio'));
foreach($data as $v){
echo $v->odt.' - '.$v->servicio.'<br>';
}
echo '<br><br>';
//with eloquent
$data = Odt::get();
foreach($data as $odt){
foreach($odt->hdt()->order_by('servicio')->get() as $hdt){
echo $odt->odt.' - '.$hdt->servicio.'<br>';
}
}
In your model you will need to explicitly tell the relation to sort by that field.
So in your odt model add this:
public function hdt() {
return $this->has_many('hdt')->order_by('servicio', 'ASC');
}
This will allow the second table to be sorted when using this relation, and you wont need the order_by line in your Fluent join statement.
I would advise against including the order by in the relational method as codivist suggested. The method you had laid is functionally identical to codivist suggestion.
The difference between the two solutions is that in the first, you are ordering odt ( all results ) by hdt.servicio. In the second you are retrieving odt in it's natural order, then ordering each odt's contained hdt by servico.
The second solution is also much less efficient because you are making one query to pull all odt, then an additional query for each odt to pull it's hdts. Check the profiler. Considering your initial query and that you are only retrieving one column, would something like this work?
HDT::where( 'odt_id', '>', 0 )->order_by( 'servico' )->get('servico');
Now I see it was something simple! I have to do the query on the second table and get contents of the first table using the function odt() witch establish the relation "belongs_to"
//solution
$data = Hdt::order_by('servicio')->get();
foreach($data as $hdt){
echo $hdt->odt->odt.' - '.$hdt->servicio.'<br>';
}
The simple answer is:
$data = Odt::join('hdt', 'odt.id', '=', 'hdt.odt_id')
->order_by('hdt.servicio')
->get(array('odt.odt as odt','hdt.servicio as servicio'));
Anything you can do with Fluent you can also do with Eloquent. If your goal is to retrieve hdts with their odts tho, I would recommend the inverse query for improved readability:
$data = Hdt::join('odt', 'odt.id', '=', 'hdt.odt_id')
->order_by('hdt.servicio')
->get(array('hdt.servicio as servicio', 'odt.odt as odt'));
Both of these do exactly the same.
To explain why this works:
Whenever you call static methods like Posts::where(...), Eloquent will return a Fluent query for you, exactly the same as DB::table('posts')->where(...). This gives you flexibility to build whichever queries you like. Here's an example:
// Retrieves last 10 posts by Johnny within Laravel category
$posts = Posts::join('authors', 'authors.id', '=', 'posts.author_id')
->join('categories', 'categories.id', '=', 'posts.category_id')
->where('authors.username', '=', 'johnny')
->where('categories.name', '=', 'laravel')
->order_by('posts.created_at', 'DESC')
->take(10)
->get('posts.*');