Saving a model with multiple foreign keys in Laravel 4 - laravel

I understand that in order to save a foreign key, one should use the related model and the associate() function, but is it really worth the trouble of going through this
$user = new User([
'name' => Input::get('name'),
'email' => Input::get('email')
]);
$language = Language::find(Input::get('language_id');
$gender = Gender::find(Input::get('gender_id');
$city = City::find(Input::get('city_id');
$user->language()->associate($language);
$user->gender()->associate($gender);
$user->city()->associate($city);
$user->save();
when one can simply do this?
User::create(Input::all());
I feel like I'm missing something here, maybe there's an even simpler and cleaner way to handle foreign keys in controllers (and views)?

You can use push() method instead which would allow you to push to related models.
This link should answer your query.
Eloquent push() and save() difference

I really don't see anything wrong at all with doing User::create(Input::all());.
Obviously you'd want some validation, but it's doing the same thing.
I think the associate() method is more useful for the inverse of your situation.
For example, say you had a form which a user could fill out to add their city to your app, and upon doing so, they should automatically be assigned to that city.
$city = City::create(Input::all()); would only achieve the first half of your requirements because the user has not yet been attached as city does not have a user_id column.
You'd then need to do something like $city->user()->associate(User::find(Auth::user()->id));

Related

How to dissociate elements from a HasMany relation?

There's the save and saveMany methods on the HasMany relation class, but where are the dissociate(Many)/detach(Many) methods? There's also no built-in way to get the inverse relationship method, so what's the best way to dissociate an array of id's/models from a HasMany relationship object.
Currently I'm using:
$hasMany = $parent->theRelationship(); // Get the relationship object.
$child = $hasMany->getRelated(); // Get an empty related model.
$key = $hasMany->getForeignKeyName(); // Get the name of the column on the child to set to NULL.
$child->findMany($IDs)->each(function($model) use ($key) {
$model->$key = NULL;
$model->save();
});
This could be alot shorter with something like:
$hasMany = $parent->theRelationship();
$hasMany->dissociate($IDs);
Bonus points if you have any official answers from Taylor as to why he hasn't implemented this, I've seen him close feature requests of this kind on GitHub.
I am not sure why there isn't a function, but to be more performant than your example, you could use the DB class like:
\DB::table('child_table')->where('parent_id', $parent->id)->update(['parent_id' => null]);
You could use detach like so;
$parent->theRelationship()->detach([1,2,3])
Where you pass an array of IDs.
From Laravel documentation:
"For convenience, attach and detach also accept arrays of IDs as input"
The performatic way (1 db update):
$partent->theRelationship()->update(['parent_id' => null]);
The readable way (multiple db updates):
$parent->theRelationship->each->parentRelationship()->dissociate();

How to create a child by one SQL-request in Laravel?

If an author has many books ("one to many" relationship) and I want to create a child by $author_id I should do this:
$author = Author::find($author_id);
$author->books()->create([...]);
But this code produces two SQL-requests as well as this:
Author::find($author_id)->books()->create([...]);
To reduce the number of SQL-requests I should add author_id field into the $fillable array in my Book model and do this:
Book::create([
'author_id' => $author_id,
...
]);
Which approach is better? As for me, the first one looks more correct, more Eloquent way, but 2 SQL-requests are too much for such simple case. Are there any other ways to make only one SQL-request without touching the $fillable array?
The old school:
$book = new Book;
$book->author_id = $author_id;
//...
$book->save();
Or you could forceCreate, which bypasses fillable:
Book::forceCreate(['author_id' => $author_id, ...]);
As for which approach is better: if you know the author_id, then 2nd (without using the relationship). But from my experience, that's rarely the case, since you usually want to check whether the related model actually exists. But if you're confident in the correctness of your input, no need for 2 queries.

Eloquent 5.4 - HasMany Object Mass Update Timestamps

really need your help here. ( I don't know what I want is possible on Eloquent )
Lets pretend this Relationship: One user can have many Childs
Note: Ignore problems in the code, this is just an example.
Now lets add some code into it.
// Return HasMany Object Instance from Eloquent.
$hasMany = $user->childs()
// Perform Mass Update.
$hasMany->update(['born_at' => Carbon::now])
So far nothing wrong with it, the first line returns an HasMany Object ( Documentation )
The problem is that Mass Updating touches my Model's timestamps ( created_at, updated_at ) and specially for this update I don't want it to do that.
Disabling it on the Model is not an option for me I do use the timestamp touch normally but I don't want to use in this case.
Neither I want to iterate over the Collection ( $user->childs ) because I have many rows to update and its an overhead to generate one query for each Model to update.
What I expect for an answer to this question: Simple, I just want an way to turn off the timestamps to do the mass updating or something like that.
( Normally on a single Model you can disable it like this: $model->timestamps = false, but this will not work here because hasMany instance does not have this attribute. )
You could set the property default of the model to false. So in you're model class you will have:
public $timestamps = false;
But this will always disable the timestamps until je use:
$model->timestamps = true;
In case someone finds this through Google:
One possible solution is to Fallback to the base QueryBuilder:
(new Child)
->newQuery()
->toBase()
->where('parent_id', $model->id)
->update([
'born_at' => Carbon::now,
]);
Of course, one could just use here something like DB::table(...)...

Seeding and pivot tables?

I'm trying to popular a pivot table with ids in a seed.
$id = DB::table('products')->insertGetId(array(
array(
'title' => 'Product A',
'published' => 1
)
));
DB::table('product_user')->insert(array(
array(
'product_id' => $id,
'user_id' => '9999999999'
)
));
Is the above the best way to do it? By getting an id via insertGetId and then putting it in the pivot table Is there a better way?
Also the above way gives an error:
[ErrorException]
preg_replace(): Parameter mismatch, pattern is a string while replacement i
s an array
I suspect $id is an array, how can i get InsertGetId to return an int?
Well, you can print_r it and seek for the id attribute, and then call $id->attribute on the second insert.
But...
"Me myself", I like to use Eloquent. The mainly reason is: 'Cause it too god damm FUN. Really, Eloquent it's one of the most beautful things I've ever seen in the programming world. I use to thought that .NET was a master piece of software (I was young and naive, though) but once a came across Laravel/Eloquent, I became so AMAZED!
That being said, in my humble opinion, use Eloquent is the best way of doing it!
I'm assuming that you have a table called products and another called users, and you have a product_user table to make the connection. Using Eloquent, you can simply do this:
$user = User::find($user_id);
$product = Product::find($product_id);
$product->user->attach($user);
...I reacomend this approach for several reasons, but the first one is: is way more readable.
Well, I hope I ain't been too prolixous on my answer, and that it hope you and others.

Laravel 4: making a combination of values/columns unique

I'm importing a bunch of csv entries in my database with Laravel 4.
I can't really point at one column that has to be unique, it's a combination of 5 columns that makes it unique. However: how does one define this in Laravel?
Option 1: schema builder
You can use the $table->unique('email') method, but that only seems to allow one column, not a combination of columns.
Option 2: Validation
Less preferable, but I could validate the model before inserting it. However, again, using 'unique:[table]' validation rules, it will return an error when just one of the column values isn't unique, not a combination of them.
Can anyone tell me how I should go about this?
I'm sure I'm missing something, but I could use a push in the right direction :-)
Thanks,
Dieter
You can combine:
$table->unique( array('email','name') );
And pretty much everything in Laravel will accept arrays to do whatever you need to with 'more than one'.
Use Schema Builder's unique() method to define your data model, as Antonio mentioned.
Additionally, if you want to use validation on your model, consider my custom Validator rule for multiple UNIQUE indexes: https://github.com/felixkiss/uniquewith-validator
You can also do this;
$table->unique(["column1", "column2"], 'uq_columns');
Which means that you will have a unique column combination of all the columns i.e. column1 and column2
I know this question is for Laravel 4, but I just came across this on searches and found a solution for Laravel >= 5.3
Here it is:
Of course, the migration may look something like
$table->unique( array('email','name') );
Then to validate this, you do not need to use custom rules, just advanced rules:
'email' => Rule::unique('users')->where(function ($query) use ($request) {
return $query->where('name', $request->name);
}),
Of course, you may want to validate name before of this. The name should be required so that you may finish with something like this:
'name' => 'required|max:255',
'email' => Rule::unique('users')->where(function ($query) use ($request) {
return $query->where('name', $request->name);
}),
I hope it helps.
You can try this
$table->string("name");
$table->string("email")->unique("name")

Resources