One to Many relation - how to implement push - laravel

I am trying to do an insert which will create a parent record for one table and then insert records that link back to the parent record into another.
In other words this: User completes Course information form, then completes a series of questions on the same page. On submission, the course information is inserted into its own table then questions are inserted into a separate one.
My Course model is this:
class CourseVerification extends Eloquent
{
public function courseverificationqanda()
{
return $this->hasMany('CourseVerificationQandA', 'id', 'verification_id');
}
My Question model is this:
class CourseVerificationQandA extends InnovedBaseModel
{
public function courseverification()
{
return $this->belongsTo('CourseVerification');
}
On form submission, my controller is doing this:
// create course verification record first
$veri = new CourseVerification;
$veri->verification_date = $input['verification_date'];
// create collection to store questions
$collection = new Illuminate\Database\Eloquent\Collection();
// loop through submitted questions and push them to the collection
for($i = 0; $i < count(Input::get('question_id')); $i++) {
$answer = new CourseVerificationQandA;
$answer->question_id = $input['question_id'][$i];
$answer->answer = $input['answer'][$i];
$answer->additional_notes = $input['additional_notes'][$i];
$collection->push($answer);
}
// add collection to quesetion relation
$veri->courseverificationqanda = $collection;
// insert both course record and questions
$veri->push();
The push method then errors and debugs the SQL command
insert into `CourseVerification`
(`verification_date`, `topic_id`, `course_id`, `verifier_id`,`iv_status`,
`verification_type`, `courseverificationqanda`, `created_by`)
values
(29/10/2014, 1294, 47, 1, 1, I, [{"question_id":"2","answer":"0","additional_notes":""},
{"question_id":"3","answer":"0","additional_notes":""},
{"question_id":"4","answer":"0","additional_notes":""}], 1)
As you can see, the assignment of the collection to $veri->courseverificationqanda is then getting treated as a table column in the SQL query when it is actually a relationship to the question table.
Any ideas?

You have a few mistakes there.
You don't assign collection to the relation. You need to load that relation (as a collection in this case) and push on it, then just save everything:
$veri = new CourseVerification;
$veri->verification_date = $input['verification_date'];
// Save the parent model before you can add its children
if ($veri->save())
{
// loop through submitted questions and push them to the collection
for($i = 0; $i < count(Input::get('question_id')); $i++) {
$answer = new CourseVerificationQandA;
// associate with the parent model
// explicitly
$answer->verification_id = $veri->getKey();
// or the eloquent way
// $answer->courseverification()->associate($veri);
$answer->question_id = $input['question_id'][$i];
$answer->answer = $input['answer'][$i];
$answer->additional_notes = $input['additional_notes'][$i];
$veri->courseverificationqanda->push($answer);
}
}
// save both course record and questions
$veri->push();
Another thing are your relations, which are both wrong:
// this is how it should look like
// CourseVerification
public function courseverificationqanda()
{
return $this->hasMany('CourseVerificationQandA', 'verification_id', 'id');
}
// QandA
public function courseverification()
{
return $this->belongsTo('CourseVerification', 'verification_id');
}
In firsts case you swapped the keys, so it would work but not the way it should.
In second case you didn't specify the foreign key at all, so Eloquent would look for courseverification_id in the table (method_name_id).

Related

Get all badges with user progress if any

I have a many to many relationship between users and achievements. Table layout is similar to this.
users
-id
-first_name
-last_name
-email
acheivements
-id
-type-name
-description
achievement_user
-id
-acievement_id
-user_id
-is_complete (boolean)
-percentage_complete (integer)
-completed_at
I can get the bulk of the stuff I want such as all achievements with type = badge for a user that are in the pivot table. I can also get a list of all the achievements that have type = badge. Where I am stuck is trying to get all achievements with type = badge along with is_complete, percentage_complete, completed_at for that given user. So if there are 8 total badges and they have started 3 of them I would want to return 8 records where 3 of them would have the information from the pivot table. I know I could grab all badges and loop through them to add on additional information but I know there is a eloquent way that would be easier.
Thank for any help.
This is in response to you recent comment.
To achieve what you want, I have introduced a new method on the User model called achievementsblock, which uses php array_map method.
I have not tested the code, but you should get the idea.
//in User model
public function achievements()
{
return $this->belongsToMany('App\Achievements')->withPivot('is_complete', 'percentage_complete', 'completed_at');
}
//in User model
public function achievementsblock()
{
$all_achievements = Achievement::all();
return array_map(function($temp_achievement){
$achievement_ids = $this->achievements()->pluck('id');
$new_temp = new \stdClass();
$new_temp->type_name = $temp_achievement->type_name;
$new_temp->description = $temp_achievement->description;
if(in_array($temp_achievement->id, $achievement_ids)){
$user_achievement = $this->achievements()->where("achievements.id","=",$temp_achievement->id)->first();
$new_temp->is_complete = $user_achievement->is_complete;
$new_temp->percentage_complete = $user_achievement->percentage_complete;
$new_temp->completed_at = $user_achievement->completed_at;
}
else {
$new_temp->is_complete = 0;
$new_temp->percentage_complete = 0;
$new_temp->completed_at = null;
}
return $new_temp;
}, $all_achievements);
}
In your controllers, you can get the achievementsblock for a user as follows:
$user_achievement_blocks = User::first()->achievementsblock();
On your belongsToMany relationship, use the withPivot function as follows:
//in User model
public function acheivements()
{
return $this->belongsToMany('App\Acheivement')->withPivot('is_complete', 'percentage_complete', 'completed_at');
}
//in Acheivement model
public function users()
{
return $this->belongsToMany('App\User')->withPivot('is_complete', 'percentage_complete', 'completed_at');
}
You need to learn about whereHas method which lets you query on relationship while getting results
In User model
// create achievements relationship
public function achievements()
{
return $this->belongsToMany(Achievement::class)
->withPivot('is_complete', 'percentage_complete', 'completed_at');
}
//then query it like shown below.
$achievements= $user->achievements;
Read more here

Laravel Eloquent Nested Relationships. Storing rows in controller variable

I have some working queries that are not ideal and I'd like to try perform them the Eloquent way to tidy it up.
I think I've got the relationships defined correctly but I can't figure out how to write the queries and store them in variables in the controller. I need to return these variables back to the view because I json encode them for use in JavaScript.
Here's what I have in my controller:
public function show($idProject)
{
$project = ProjectsModel::with('user')->where('idProjects','=',$idProject)->first();
$objsettings = ObjSettingsModel::where('idObjSettings','=',$project['ObjSettingsID1'])->first();
$obj = ObjModel::where('idObjs','=',$objsettings['Objs_idObjs'])->first();
return view('project')->with('project',$project)->with('obj',$obj)->with('objsettings',$objsettings);
}
The naming conventions are a bit off so here's what this does.
I pass the $idProject to the controller from a link on my index page where I've looped through and paginated all rows from the Projects table.
The first query finds the project row where it's id (idProjects) matches the variable passed from the index page via the link (idProject). I've also successfully pulled the related user row from the user table using an eloquent relationship.
The next query pulls from an ObjSettings table which stores a number of settings values for an object which is shown on the page. I match the idObjSettings column of the ObjSettings table to the previously pulled $project['ObjSettingsID1'] which is essentially the foreign key in the projects table. There can be several ObjSettings for each Project.
The 3rd query pulls from the Obj table which stores details about an object. These are static details on objects such as name or size for example. I match the idObjs column to the previously pulled $objsettings['Objs_idObjs'] which is the foreign key in the ObjSettings table. One Obj can have many ObjSettings which are used in other Projects.
Here's how I'm passing the php variables to JS:
<script>var obj = #json($obj);</script>
<script>var objsettings = #json($objsettings);</script>
Here are my relationships
ProjectsModel
public function user()
{
return $this->belongsTo('App\User', 'Username', 'id');
}
public function objsettings()
{
return $this->hasMany('App\ObjSettingsModel', 'idObjSettings', 'ObjSettingsID1' );
}
ObjSettingsModel
public function objs()
{
return $this->belongsTo('App\ObjsModel', 'Objs_idObjs', 'idObjs');
}
public function projects()
{
return $this->belongsTo('App\ProjectsModel', 'ObjSettingsID1', 'idObjSettings' );
}
ObjModel
public function objsettings()
{
return $this->hasMany('App\ObjSettingsModel', 'idObjs', 'Objs_idObjs');
}
I've tried a whole range of queries such as:
$project = ProjectsModel::with('user')->with('objsettings.objs')->where('idProjects','=',$idProject)->first();
$objsettings = $project->objsettings;
$obj = $project->objsettings->objs;
but I keep running into issues such as "Property [objs] does not exist on this collection instance." I suppose I'm returning multiple rows in this case? Any help would be appreciated.
You need to loop through objsettings
$project = ProjectsModel::with('user')->with('objsettings.objs')->where('idProjects','=',$idProject)->first();
$objsettings = $project->objsettings;
foreach($objsettings as $objsetting){
$objs = $objsetting->objs;
}

How to concatenate an auto increment value before inserting into database in laravel

In my table I have a field that needs to be the same value as the id field which is auto increment and then the value is concatenated with certain strings.
How do I do this in store function of my controller?
I'm thinking of doing update right after insertion but I don't know how to get the inserted id, and that might be a problem if two or more users doing the same insertion into database at the same time.
Thanks
UPDATE:
in my store controller:
public function store(Request $request)
{
$kartukeluarga = new kartukeluarga();
$kartukeluarga->nomorkk = $request->input("nomorkk");
$kartukeluarga->kepalakeluarga = $request->input("kepalakeluarga");
$kartukeluarga->alamat = $request->input("alamat");
$kartukeluarga->save();
return redirect()->route('kk.index')->with('message', 'Item created successfully.');
}
the new field I want to add is 'rekamMedik' which doesn't need an input form but automatically concatenate the id and this string "kk-value of the id"
You can save details and then update that column.
If you have model Test.
$test = new Test();
$test->column_1 = "abc";
$test->column_2 = "def";
$test->column_x = ""; // Set Empty to the column where you will save id later
$test->save();
$test->column_x = "string".$test->id;
$test->save();
This might work.

How to add data to relative table Laravel?

I have model Object and model Prototype.
Incoming data($_POST) is array prototype_id[]:
prototype_id[] = 1;
prototype_id[] = 2;
prototype_id[] = 3;
That should be saved in table ObjectPrototypes. No i dont have this model.
So, in controller I do:
$object = new Object();
$object->name = "Object";
How to add/attach prototype_id[] to saved object $object?
Important:
Object can has one or more prototypes
One (quick) solution could be to do raw insert on this table. Assuming pivot table name is object_prototype, here is what you could do:
$object = new Object();
$object->name = "Object";
$object->save();
foreach($request->input('prototype_id') as $p) {
\DB::table('object_prototype')->insert([
'object_id' => $object->id,
'prototype_id' => $prototype
]);
}
It should work, thought it's not really Laravelish.
You may also work with pivot, but it requires more boilerplate. You may have a look here: https://laravel.com/docs/5.4/eloquent-relationships#updating-many-to-many-relationships and here: https://laravel.com/docs/5.4/eloquent-relationships#one-to-many-inverse That's what I do usually but it's a bit too long for a short answer and I think Laravel Doc is more complete.

Laravel Eloquent: Store model together with related submodels

i got the models Exam and Question. Simplified code:
class Exam {
public function questions() {
return $this->hasMany('Question');
}
// ...
}
class Question {
public function exam() {
return $this->belongsTo('Exam');
}
// ...
}
Now i wanna upload a file with exam data. The parser class shall create the exam without saving it. Then gradually the questions should be created and added to the exam. At the end everything should be saved in a transaction.
class ExamParser {
$exam = new Exam();
// ...
while ($linesRemaining) {
$question = new Question();
// ...
$exam->questions[] = $question; // something like this?
}
$exam->saveTogetherWithQuestions(); // how do i realize this?
}
I know basically how to save related models but not how I can just relate them and save the whole construct later.
Create an array of Question first. Then create an Exam, and save all questions via Exam's relations. Put all codes in a transaction.
Updated: Moved model creation out of transaction execution.
$questions = [];
while ($linesRemaining) {
$question = new Question();
// ...
$questions[] = $question;
}
$exam = new Exam();
// ...
DB::transaction(function() {
$exam->save();
$exam->questions()->saveMany($questions);
});
Important note that you have to save Exam before attach Question to it. Parent Exam must have its own primary key first (by saving itself) to allow attaching success. Otherwise, you will have Question saved without parent's id.

Resources