How to dynamically disable and enable foreign key check in laravel? - php

I am trying to move entries from my old tables to the new one with the updated schema. The problem is, I have to move content from 10 tables with old config to 10 tables with the new config.
I am doing this with the help of console command. When I add the new table and execute the command, I get Duplicate entry error for the tables that already have data which is obvious.
When I try to use DB::connection('mysql_old')->table('users')->truncate();, It throws 1701 Cannot truncate a table referenced in a foreign key constraint Errror which is obvious too!
Here is how I am moving entries from old tables to the new one.
$entries = DB::connection('mysql_old')->table('users')->get();
DB::table('users')->truncate();
foreach($entries as $entry){
$user = \App\User::create([
'name' => $entry->name,
'email' => $entry->email,
'status' => $entry->status,
'credits' => $entry->credits,
'role' => $entry->user_role,
'email_subscription' => $entry->email_subscription,
'activation_key' => $entry->activation_key,
'password' => $entry->password,
'remember_token' => $entry->remember_token,
'created_at' => $entry->created_at,
'updated_at' => $entry->updated_at
]);
}
The only solution is to disable foreign key check before truncate and enable it again after truncate (I think). It is a relational database as obvious. So, is there any better way to complete this task?
I thought about giving a try to move entries from old table to the new one in a relational way but it is not possible in this case.
I can execute the command php artisan migrate:refresh every time the command is executed. But here is the problem with that, There are more than 25 tables and It takes about 20-30 seconds to complete migrate:refresh.
I am really confused how to get this done. Is there any proper or standard way?

You can do this:
Schema::disableForeignKeyConstraints();
// Your database operations go here..
Schema::enableForeignKeyConstraints();

Finally, I found the solution to turn off and turn on the foreign key check. Here is how I moved information from old table to new one.
// Disable foreign key checks!
DB::statement('SET FOREIGN_KEY_CHECKS=0;');
// Move users from old table to the new one
$entries = DB::connection('mysql_old')->table('users')->get();
DB::table('users')->truncate();
foreach($entries as $entry){
$user = \App\User::create([
'name' => $entry->name,
'email' => $entry->email,
'status' => $entry->status,
'credits' => $entry->credits,
'role' => $entry->user_role,
'email_subscription' => $entry->email_subscription,
'activation_key' => $entry->activation_key,
'password' => $entry->password,
'remember_token' => $entry->remember_token,
'created_at' => $entry->created_at,
'updated_at' => $entry->updated_at
]);
}
// Enable foreign key checks!
DB::statement('SET FOREIGN_KEY_CHECKS=1;');
It worked!

if you have migration table just make index to attribut like this:
just make ->index
like this
$table->unsignedBigInteger('city_id')->index();
$table->foreign('city_id')->references('id')
->on('cities')->onDelete('cascade');

Related

Laravel prevent data duplicate in database

I want to prevent data duplicate in database when user applies a job post multiple times.
I tried firstOrNew() like...
$apply = Applies::firstOrNew(
['user_id' => Auth::id()],
['posts_id' => request('id')]);
$apply->save();
But this time user can't apply other posts anymore .
Edit 1
I tried it but doesn't do anything.
$apply = Applies::firstOrNew(
['user_id' => Auth::id()] && ['posts_id' => request('id')],
['user_id' => request(Auth::id())],
['posts_id' => request('id')]);
$apply->save();
The first array is the "attributes", the where conditions for the look up. You would need to have the user_id and post_id in that array:
Applies::firstOrNew([
'user_id' => ...,
'post_id' => ...,
]);
This will cause it to search for that combination and only if it can't find it will it create a new (non-existing) model instance and fill it with those "attributes".

Unique constraint in laravel by multiple fields

I have this table :
In the code I put :
$id = request()->route()->parameter('keyword')?->id;
return [
'key' => ['required', 'unique:keywords,key,project_page_id,'.$id],
];
If I try to add a new item with key addButton and project_page_id = 5, I get 422 error with message : The key has already been taken.. I want to have unique key by project_page_id, how can i get to this ?
Laravel has validation objects, where you get more powerful options. It has a unique validation object, and you can tweak the query, where i would think you want the following logic, to also check for the project_page_id.
'key' => ['required', (new Unique('keywords', 'id'))->where('project_page_id', $id)],

Database Relationship - Laravel

I am having issues inserting data into my database tables. I have users table which is has a relation of one to many to school table and region table. That is, users table has foreign keys of school and region tables.
I have created the relations correctly but then when i try to insert values into the database, i get the error.
PS: I have one user(admins) in the user table (the school_id and region_id was inserted accurately) already which i saved through RegisterController. Now, when i sign in with the user(admin) already saved, i want to save other users through the UserController but then i get the error above.
What am i not doing right?. I am only a beginner with database relationship and laravel
General error: 1364 Field 'school_id' and `region_id`doesn't have a default value
meaning the field cannot be empty.
UserController
public function store(UserFormRequest $request)
{
$user = new User(array(
'name' => $request->get('name'),
'email' => $request->get('email'),
'password'=> $request->get('password'),
'phone' => $request->get('phone')
));
$user->save();
}
there are three solutions to your problem.
1
public function store(UserFormRequest $request)
{
$user = new User(array(
'name' => $request->get('name'),
'email' => $request->get('email'),
'password'=> $request->get('password'),
'phone' => $request->get('phone'),
'school_id'=>'you must pass value here',
'region_id'=>'you must pass value here'
));
$user->save();
}
2 Go to phpmyadmin make your school_id,region_id default value NULL. so you are good to go.
MySQL is telling you that those fields can't be empty because they aren't nullable.
To make those fields nullable() in your users migration:
// Your create users table migration
$table->integer('school_id')->nullable();
$table->integer('region_id')->nullable();
(You can add any other properties to these - like index(), unsigned(), or foreign keys, etc.)

Inserting with relationships in laravel

I am facing a problem in Laravel 5.3 that I looked the docs and also searched web but didn't find anything on it.
I am using Laravel Relationships to join two tables. Now I want the data to be inserted on both the tables at the same time after the user submits a form. The catch in this is the first table is the primary one say "users" and second one "xyz" belongsTo the first table. The table "xyz" contains "users_id" column that connects both the tables. And obviously "users_id" is the "id" column of "users" table.
Now the problem arriving is that I want to insert the data in "users" table (that is easily done) and "xyz" table at the same time. The User::create() function will create the user data easily and it is working also but for inserting the data in "xyz" table I will be needing the "user_id" column data and ID will not be generated until the user is created as ID column has Auto-Increment attribute activated.
Code:
$user = new User;
$inputArry = array('data1' => $request['field1'],
'data2' => $request['field2'],
'data3' => $request['field3'],
);
$user->create($inputArry);
$user->xyz()->create([
'user_id' => $user->id,
'name' => $request['name'],
'about' => $request['desc'],
'tag' => $request['tag'],
]);
Above is the code that I am using for this purpose but it is giving me a error.
Error:
QueryException in Connection.php line 761:
SQLSTATE[23000]: Integrity constraint violation: 1048 Column 'soft_id' cannot be null (SQL: insert into `xyz` (`user_id`, `name`, `about`, `tag`, `updated_at`, `created_at`) values (, John, I am John, dev, 2016-11-09 21:01:29, 2016-11-09 21:01:29))
One way of inserting related table is using relations as:
$user = User::create($user_inputs);
$xyz = $user->xyz()->create($xyz_inputs);
It will automatically fills the user_id in the xyz table.
If you need insert many items, use createMany or saveMany method.
For example:
$post = App\Post::find(1);
$post->comments()->createMany([
[
'message' => 'A new comment.',
],
[
'message' => 'Another new comment.',
],
]);
In the offical laravel docs:
https://laravel.com/docs/5.6/eloquent-relationships#inserting-and-updating-related-models
You can create them like this instead of saving them on same time...
$id = User::create($input_arr)->id;
Xyz::create([
'user_id' => $id,
...
]);

Adding a conditional value to extra column in pivot table from array in Laravel

I know the Question title is a bit murky, but here's what I'm trying to do:
I'm retrieving a list of groups that a user belongs to from a third party api. In some cases, the user will be an 'admin' for a group and other times, just a 'member'.
Specifics aside, I'm calling a method on my api class from my controller that hits the api, retrieves the user's groups, decides if they are an 'admin' or not, then returns an array of arrays with each group's information including a 'role' key that denotes whether or not they are an 'admin'. So my returned array looks something like this:
[
0 => [
'unique_id' => 1243657,
'name' => 'Group1',
'city' => 'Bluesville',
'state' => 'IN',
'role' => 'admin'
],
1 => [
'unique_id' => 4324567,
'name' => 'Group2',
'city' => 'New Curtsbourough',
'state' => 'WI',
'role' => 'member'
],
2=> [
'unique_id' => 87463652,
'name' => 'Group3',
'city' => 'Samsonite',
'state' => 'MN',
'role' => 'member'
]
]
Now, I need to take those groups and store them in the database, which I'm doing by checking first that the group doesn't exist in the database, then adding it if needed. Of course, I'm leaving off the role, as it is only relevant to the current user.
Next, I need to connect the current user to these groups that were just retrieved. I have a pivot table set up that currently holds the user_id and group_id.
The question is, how to best handle this. Before I decided that I needed to know whether or not a member was an 'admin' or not, I simply had my 'createGroups' method return an array of primary keys to me, then passed that array to a call to
$user->groups()->sync($array_of_ids);
However, with the added 'role' information, it's not as cut and dry.
Basically, at this point in the lifecycle, I have access to an array of groups that contains a field 'role'. My thinking says to add a 'role' field to the pivot table, which would then contain 'user_id', 'group_id' and 'role'. This means I'll not only need the $groups array with the retrieved groups, but the ids of those groups as they pertain to my database.
I could make something work, but I'm afraid it would be extremely messy and inefficient.
Thoughts anyone??
Ok, as happens many times on Stackoverflow, I've come to a solution for my own question. I'm posting so that in the off-chance someone stumbles upon my question needing to do something similar, they can at least see how one person handled it.
According to the Laravel docs, if you want to sync relationships with an added column, you need to call sync in the following way:
$user->groups()->sync([
1 => ['role' => 'admin'],
2 => ['role' => 'member'],
3 => ['role' => 'member']
]);
So before I could sync, I needed an array that resembled the array that is being passed to 'sync'.
Since I had an array of 'groups' that included a field called 'role' for each group, I created a 'createGroups' method that basically looped over the $groups array and called the 'insertGetId' method that Laravel provides. This method persists the object to the database and returns the primary key of the created record. For my 'createGroups' method, I did the following:
public function createGroups($groups)
{
$added = array();
foreach($groups as $group){
$id = $this->createGroup($group);
$added[$id] = ['role' => $group['role']];
}
return $added;
}
So as I'm inserting 'groups' into the database, I'm building up the array that is needed by the 'sync' method. Since the 'createGroup' method uses Laravel's 'insertGetId' method, it returns the primary key for that group. Then I use that id as the key to the array. After all groups are inserted, my 'added' array that is returned to my controller, looks like this:
[
1 => ['role' => 'admin'],
2 => ['role' => 'member'],
3 => ['role' => 'member']
]
which is exactly what the 'sync' method needs to do it's thing.
Happy coding!

Categories