is there a way to select only certain columns when doing eager-loading in laravel?
$columns = ['col_1', 'col_2', 'col_3'];
$model = MyModel::findOrFail(Input::get('id'), $columns);
$model->load('position');
In My model I Have defined:
public function position() {
return $this->belongsToMany('Position', 'mymodel_positions')->withTimestamps();
}
When I don't provide the $columns parameter like MyModel::findOrFail(id). Positions are
loaded correctly. However if I do provide $columns, the positions ale always empty.
What I want: eagerly load a relation AND specify the columns (Not the ones in the loaded relation). I'm sure It's possible, but i simply don't get it...
Hope for your help.
tldr; You always need to SELECT primary key and corresponding foreign key of the relation.
Most likely this:
$columns = ['col_1', 'col_2', 'col_3'];
doesn't contain your model's primary key, so Eloquent is unable to match related models.
That being said, all you need is:
$columns = ['id', 'col_1', 'col_2', 'col_3'];
or whatever field is the primary key.
Related
I'm trying to delete a record with two primary keys, using Eloquent - Laravel.
This is my model
class Like extends Model
{
//protected $primaryKey = ['crdid', 'usrid'];
public $timestamps = false;
use HasFactory;
}
Controller
try{
$dellike = Like::where('crdid', '=', $like->crdid, 'and')
->where('usrid', '=', $like->usrid)->first();
$dellike->delete();
}
catch(Exception $e){
return $e->getMessage();
}
Table definition
Schema::create('likes', function (Blueprint $table) {
$table->biginteger('crdid');
$table->biginteger('usrid');
$keys = array('crdid', 'usrid');
$table->primary($keys);
});
However, it gives me the below error;
Column not found: 1054 Unknown column 'id' in 'where clause' (SQL: delete from `likes` where `id` is null)
I believe the 'id' is the default primary key used by Eloquent, but I'm not using it.
How should I define that, I'm not using the default primary key? or what is the correct way to delete the record with composite primary keys? Any help would be highly appreciated.
As Laravel Eloquent Docs says:
Eloquent requires each model to have at least one uniquely identifying "ID" that can serve as its primary key. "Composite" primary keys are not supported by Eloquent models. However, you are free to add additional multi-column, unique indexes to your database tables in addition to the table's uniquely identifying primary key.
So you can't use some methods of eloquent.
But seeing your code, looks like you are trying to create a model for a relationship pivot table (many to many between user and crd (what is crd?! no reason to abreviatte here.)
Try defining a many to many relationship on your user and crd model.
Also laravel naming convention for key columns is: model_name_id for a model named ModelName
But, just to delete, you can skip the first in your query:
Like::where('crdid', '=', $like->crdid, 'and')
->where('usrid', '=', $like->usrid)->delete();
This happens because when you delete a model (your attempt) laravel uses the primary key field (defaults to id) as where condition. But manually querying, you are defining the where clauses of the delete.
Just create additionnal column named id in the table as unique with auto-increment option. Eloquent will use it to delete each row with delete function.
Example with Laravel 5.5 :
$logement = Logement::findOrFail($id);
$commodites = Commodites_logement::all()->where('logement_id','=',$logement->id);
foreach ($commodites as $commodite) {
$commodite->delete();
}
I'm used to it but today this problem makes me weak..;;
class Market {
// ..
public function ttl()
{
return $this->ttlRelation()->firstOrCreate(
['market_id' => $this->id],
['tier' => 0, 'direction'=>0]
);
}
}
The Market model has one TTL model. I know that firstOrCreate method finds an item as first given array and if it doesn't exists create a new one as persist, returns it.
Besides, its mass-assignment so I filled up $fillable property on ttl model..
class TradingTacticalLayer extends Model
{
public $timestamps = false;
protected $fillable = ['direction', 'tier'];
}
..and I'm getting SQLSTATE[HY000]: General error: 1364 Field 'direction' doesn't have a default value (SQL: insert into "trading_tactical_layer_test" ("tier", "market_id") values (0, 1)) message. I cannot understand why this method won't filled up insert field list proper way. I expect, if I edit $fillable property as ['direction'], SQL would implode ("direction") as insert field and it doesn't.
In general, from my experience, I just set those fields as nullable or manually set a default value. At this time, I want to know why this weird happens and what am I doing wrong.
Well, probably, optimize:clear solve the problem.
I still don't know what makes this error but if you experience mismatch between $fillable property and inserting field list, optimize:clear is an option anyway..
When I eagerly load a straightforward Eloquent relationship I can limit the columns retrieved using the following syntax:
MyModel::with(myRelation:id,col_2,col_3);
When I want to constrain the members of the relationship, I do this:
MyModel::with(['myRelation' => function ($query) {
$query->where([
['field_1', 'a_value'],
['field_2', 'b_value']
]);
}]);
But that loads all columns of the related models. I tried replacing the key myRelation with the full syntax, myRelation:id,col_2,col_3, but it throws an error which says the relation name is not found.
I also tried adding the following methods to the $query:
->select('id', 'col_2', 'col_3')
or
->addSelect('id', 'col_2', 'col_3')
or
->get('id', 'col_2', 'col_3')
None of these were successful.
One option could be keeping all the columns (except id, col_2, col_3) hidden in your original model MyRelation is pointing to.
protected $hidden = ['col_4', 'col_5',...];
You could also change the relationship definition in your model MyModel:
public function myRelation()
{
return $this->belongsTo('MyRelation')->select(array('id', 'col_2', 'col_3'));
}
But, in general, this should work:
MyModel::with(array('myRelation'=>function($query){
$query->select('id','col_2', 'col_3)
->where([
['field_1', 'a_value'],
['field_2', 'b_value']
]);
}))->get();
Remember, you have to include the joining key (say id) in the select list above.
Ok, so to get this to work, I had to:
include the foreign_key in the list of selects (thanks, #ankitPatel), and
pass the parameters to the select statement as an array, as follows:
->select(['id','foreign_key','col3','col4'])
I have a collection of objects. Let's say the objects are tags:
$tags = Tag::all();
I want to retrieve a certain attribute for each tag, say its name. Of course I can do
foreach ($tags as $tag) {
$tag_names[] = $tag->name;
}
But is there a more laravelish solution to this problem?
Something like $tags->name?
Collections have a lists method similar to the method for tables described by #Gadoma. It returns an array containing the value of a given attribute for each item in the collection.
To retrieve the desired array of names from my collection of tags I can simply use:
$tags->lists('name');
Update: As of laravel 5.2 lists is replaced by pluck.
$tags->pluck('name');
More specifically, the laravel 5.2 upgrade guide states that "[t]he lists method on the Collection, query builder and Eloquent query builder objects has been renamed to pluck. The method signature remains the same."
Yep, you can do it nice and easily. As the Laravel 4 Documentation states, you can do
Retrieving All Rows From A Table
$users = DB::table('users')->get();
foreach ($users as $user)
{
var_dump($user->name);
}
Retrieving A Single Row From A Table
$user = DB::table('users')->where('name', 'John')->first();
var_dump($user->name);
Retrieving A Single Column From A Row
$name = DB::table('users')->where('name', 'John')->pluck('name');
Retrieving A List Of Column Values
$roles = DB::table('roles')->lists('title');
This method will return an array of role titles.
You may also specify a custom key column for the returned array:
$roles = DB::table('roles')->lists('title', 'name');
Specifying A Select Clause
$users = DB::table('users')->select('name', 'email')->get();
$users = DB::table('users')->distinct()->get();
$users = DB::table('users')->select('name as user_name')->get();
EDIT:
The above examples show how to access data with the help of Laravel's fluent query builder. If you are using models you can access the data with Laravel's Eloquent ORM
Because Eloquent is internaly using the query builder, you can without any problem do the following things:
$tag_names = $tags->lists('tag_name_label', 'tag_name_column')->get();
which could be also done with:
$tag_names = DB::table('tags')->lists('tag_name_label', 'tag_name_column')->get();
Here are a few snippets from my own experimentation on the matter this morning. I only wish (and maybe someone else knows the solution) that the Collection had a $collection->distinct() method, so I could easily generate a list of column values based on an already filtered collection.
Thoughts?
I hope these snippets help clarify some alternative options for generating a list of unique values from a Table, Collection, and Eloquent Model.
Using a Collection (Happy)
/**
* Method A
* Store Collection to reduce queries when building multiple lists
*/
$people = Person::get();
$cities = array_unique( $people->lists('city') );
$states = array_unique( $people->lists('state') );
// etc...
Using an Eloquent Model (Happier)
/**
* Method B
* Utilize the Eloquent model's methods
* One query per list
*/
// This will return an array of unique cities present in the list
$cities = Person::distinct()->lists('city');
$states = Person::distinct()->lists('state');
Using an Eloquent Model PLUS Caching (Happiest)
/**
* Method C
* Utilize the Eloquent model's methods PLUS the built in Caching
* Queries only run once expiry is reached
*/
$expiry = 60; // One Hour
$cities = Person::remember($expiry)->distinct()->lists('city');
$states = Person::remember($expiry)->distinct()->lists('state');
I would love to hear some alternatives to this if you guys have one!
#ErikOnTheWeb
You could use array_column for this (it's a PHP 5.5 function but Laravel has a helper function that replicates the behavior, for the most part).
Something like this should suffice.
$tag_names = array_column($tags->toArray(), 'name');
I want to return a JSON of an Eloquent model, but I'd like to change the array keys. By default they are set as the table field names, but I want to change them.
For example if I have a users table with two fields : id and user_name
When I return User::all(); I'll have a JSON with "[{"id" => 1, "user_name" => "bob}] etc.
I'd like to be able to change user_name to username. I haven't found the way to do it without an ugly foreach loop on the model.
I'm not sure why you would want to do this in the first place and would warn you first about the structure if your app/would it be better to make things uniform throughout.. but if you really want to do it.. you could do:
$user = User::find($id);
return Response::json(array('id' => $user->id, 'username' => $user->user_name));
That will return a JSON object with what you want.
You can also change the name of the key with:
$arr[$newkey] = $arr[$oldkey];
unset($arr[$oldkey]);
Just have a look at robclancy's presenter package, this ServiceProvider handles those things you want to achieve.
GITHUB LINK
Just set the $hidden static for you model to the keys you want to hide:
class User extends Eloquent
{
public static $hidden = 'id';
}
and name them the way you like with get and set functons.