UpdateExistingPivot method doesn't work - php

I'm trying to update a value in a pivot table. This is my method :
public function updateStatus(Event $event)
{
$this->authorize('updateStatus', $event);
$newStatus = Input::get('status');
$actualPivot = $event->guests()->where('user_id', Auth::id())->first()->pivot;
$id = $actualPivot['id'];
$status = $actualPivot['status'];
if ($newStatus != $status)
{
dd($event->guests()->updateExistingPivot($id, ['status' => $newStatus]));
}
return back();
}
I've checked with HeidiSQL, the row isn't updated how it should be. I've also tried this solution, but it doesn't update the row, it creates a new one. There is the dd() with this method:
array:3 [▼
"attached" => array:1 [▼
0 => 1
]
"detached" => []
"updated" => []
]
This is my guests() relation defined in the Event model:
public function guests()
{
return $this->belongsToMany('App\User')
->using('App\Invitation')
->withPivot('id', 'status')
->withTimestamps();
}
I don't know why the updateExistingPivot() method doesn't work. I hope you can help.

You must use the guest_id or whatever is the name for your foreign key of App\Invitation instead of your pivot id in order to update the existing record, otherwise you have not a relation for the current event that matches your pivot id.
$id = $actualPivot['guest_id']; // change guest_id for your foreig key name of \App\Invitation
$status = $actualPivot['status'];
if ($newStatus != $status)
{
dd($event->guests()->updateExistingPivot($id, ['status' => $newStatus]));
}

Related

Laravel pivot table (Retreive all items that belongs to)

I have an Item table that contains fields
I have a template table that groups the fields with a pivot table
I have a page table that contains the template being used
I need to retrieve the field list associated with the template being used by the page
$gestionPage->load('template', 'parent');
$page = $gestionPage->get()[0];
$formulaire = Item::find($page->template_id)->with('itemsTemplate')->get();
foreach($formulaire as $k=>$v){
$data[] = $v->nom;
}
dd($data);
So fare, $data contain all fields (Not just the one that belongs to the right template)
array:3 [▼
0 => "Titre"
1 => "Texte"
2 => "wysiwyg"
]
2 => "wysiwyg" is not part of the template ...
It should output
array:2 [▼
0 => "Titre"
1 => "Texte"
]
I don't understand how the pivot systeme works ...
In the item model
public function itemsTemplate()
{
return $this->belongsToMany(Template::class);
}
In the item model
public function items()
{
return $this->belongsToMany(Item::class);
}
How to easaly retreive the list of items a template id owns?
Try this here:
public function templates()
{
return $this->belongsToMany(Template::class)->withPivot(array $fields);
}
public function items()
{
return $this->belongsToMany(Item::class)->withPivot(array $fields)
}
then you can access the pivot table fields like this here:
$item->pivot->field
I made some changes to your code. See if it works now. Read how many to many relationships works in Laravel from this link https://laravel.com/docs/8.x/eloquent-relationships#many-to-many
$gestionPage->load('template', 'parent');
$page = $gestionPage->get()[0];
$formulaire = Item::find($page->template_id)->templates()->get();
dd($formulaire);
//Rename this from itemsTemplate to templates - just because is plural
public function templates()
{
return $this->belongsToMany(Template::class);
}
public function items()
{
return $this->belongsToMany(Item::class);
}

Insert a new record if not exist and update if exist, laravel 5.4

Is there any way to insert a new record if doesn't exist and update the record if exist? the following is the code i using.
public function store(Request $request)
{
$inputs = $request->all();
$this->validate($request,[
'course_code'=>'required'
]);
$batch = $request->batch;
$students = User::select('student_id')->where('batch', $batch)->get();
$course_codes = $inputs['course_code'];
$data=[];
foreach ($students as $student) {
foreach ($course_codes as $course_code) {
$data[]=[
'batch' => $batch,
'student_id' => $student->student_id,
'semester' => $inputs['semester'],
'course_code' => $course_code,
"created_at" => \Carbon\Carbon::now(), # \Datetime()
"updated_at" => \Carbon\Carbon::now() # \Datetime()
];
}
}
DB::table('assign_batches')->insert($data);
return redirect('/admin/assign/batch/create')->with('message', 'A batch has been assigned to courses successfully!');
}
Here is my output when I inserted same records again.
But I want one Student Id may have many Course Code but can not be duplicate. So I want to check if student has same course or courses then skip or update it and insert new records.
Please help me.
Thanks.
Check id if exist then update otherwise insert
if(isset($request->id)){
DB::table('assign_batches')->where("id", $request->id)->update($data);
}else {
DB::table('assign_batches')->insert($data);
}
Use firstOrCreate / firstOrNew
$students = User::firstOrNew(['student_id' => $request->student_id, 'course_code' => $request->course_code]);
$students->foo = $request->foo;
$students->save();
for example if you have a table for every each user to vote once or update his vote;
//give the record to use
$record = request('vote');
//checkif there is a record for user in model
$row = Uservoting::where('user_id',Auth->user()->id)->first;
//if true
if($row!==null){
//update
$row::update(['vote' => $record]);
//if there isn't in model
}else{
//create a new row and insert the value
$new_row = new Uservoting();
$new_row->user_id = Auth->user()->id ;
$new_row->vote = $record ;
$new_row->vote();`
}
hope this work:) {{--if you found any bug just tell me--}}

Inserting array using Laravel Eloquent

I have an array which I combined using array_combine. I am trying to store each array KEY AND VALUE into a child table. However, I can't figure out to do this. Please help!
HERE'S AN EXAMPLE OF A RETURNED ARRAY
array:2 [▼
"Design" => "Pattern"
"Brand" => "Sony"
]
PRODUCT MODEL
public function productAttributes()
{
return $this->hasMany('App\ProductAttribute');
}
PRODUCT ATTRIBUTE MODEL
protected $fillable = [
'attribute_name', 'attribute_value', 'used_as_filter'
];
public function product()
{
return $this->belongsTo('App\Product');
}
PRODUCT CONTROLLER
$product = new Product();
$product->category_id = $request->category_list;
$product->name = $request->name;
$product->price = $request->price;
$product->save();
/**Optional Data**/
if ($request->has('attribute_name')){
$attributes = array_combine($request->input('attribute_name'), $request->input('attribute_value'));
$product->productAttributes()->create($attributes);
}
When I run this, I get one row inserted into the product table, and one row inserted into the product_attribute table. However, the columns attribute_name and attribute_value is blank.
$attributes = array_combine($request->input('attribute_name'), $request->input('attribute_value'));
collect($attributes)->each(function ($value, $name) use ($product) {
$product->productAttributes()->create([ 'attribute_name' => $name, 'attribute_value' => $value, ]);
});

Laravel updateOrCreate And Increment Value

In my app, i'm saving customer interests depending on viewing products, adding to carts, orders and category viewing.
Now here is my customerInterestController
$customerInterest = [
'user_id' => Auth::user()->id,
'interest' => $category_id,
];
$data = [
'cart_interest' => ($column == 'cart') ? 1 : 0,
'order_interest' => ($column == 'order') ? 1 : 0,
'category_interest' => ($column == 'category') ? 1 : 0,
'view_interest' => ($column == 'view') ? 1 : 0,
];
try{
(new CustomerInterest)->insertCustomerInterest($customerInterest, $data);
}catch(Exception $e){
dd($e);
}
And here is my customerInterest Model
public function insertCustomerInterest($customerInterest = [], $data = []){
return $this->updateOrCreate($customerInterest, $data);
}
There is no problem with inserting database. It works.
My Table
id
user_id
interest
cart_interest
order_interest
category_interest
view_interest
user_id and interest columns are composite unique index.
If user_id, interest columns exists
i want to update data
if not i want to insert data.
I use updateOrCreate method but couldn't increase the values in $data array.
How can i do that ?
I solved my problem.
I removed $data array
Here is my code for insert or update
(new customerInterestController)->insertCustomerInterest('category', $category->id);
Here is my model
public function insertCustomerInterest($customerInterest = [], $column){
return $this->updateOrCreate($customerInterest)->increment($column.'_interest');
}

Get all relationships from Eloquent model

Having one Eloquent model, is it possible to get all its relationships and their type at runtime?
I've tried taking a look at ReflectionClass, but I couldn't find anything useful for this scenario.
For example, if we have the classic Post model, is there a way to extract relationships like this?
- belongsTo: User
- belongsToMany: Tag
To accomplish this, you will have you know the names of the methods within the model - and they can vary a lot ;)
Thoughts:
if you got a pattern in the method, like relUser / relTag, you can filter them out
or loop over all public methods, see if a Relation object pops up (bad idea)
you can define a protected $relationMethods (note: Laravel already uses $relations) which holds an array with method.
After calling Post->User() you will receive a BelongsTo or 1 of the other objects from the Relation family, so you can do you listing for the type of relation.
[edit: after comments]
If the models are equipped with a protected $with = array(...); then you are able to look into the loaded relations with $Model->getRelations() after a record is loaded. This is not possible when no record is loaded, since the relations aren't touched yet.
getRelations() is in /vendor/laravel/framework/src/Illuminate/Database/Eloquent/Model.php
But currently it doesn't show up in the api at laravel.com/api - this is because we got newer version
Like Rob stated. It is a bad idea to loop through every public method and check out if a relation is returned.
Barryvdh uses a Regex based approach in his very popular Laravel-ide-helper:
https://github.com/barryvdh/laravel-ide-helper/blob/master/src/Console/ModelsCommand.php
You just have to filter the properties you receive after calling getPropertiesFromMethods like this (untested example):
class classSniffer{
private $properties = [];
//...
public function getPropertiesFromMethods($model){
//the copied code from the class above (ModelsCommand#getPropertiesFromMethods)
}
public function getRelationsFrom($model){
$this->getPropertiesFromMethods($model);
$relations = [];
foreach($this->properties as $name => $property){
$type = $property;
$isRelation = strstr($property[$type], 'Illuminate\Database\Eloquent\Relations');
if($isRelation){
$relations[$name] = $property;
}
}
return $relations;
}
}
Is there a cleaner way of doing that without touching the Models?
I think we have to wait for PHP7 (Return Type Reflections) or for a new Reflection Service from Taylor ^^
I've been working on the same thing lately, and I don't think it can effectively be done without Reflection. But this is a little resource-intensive, so I've applied some caching. One check that's needed is to verify the return type, and pre-php7, that can only be done by actually executing each method. So I've also applied some logic that reduces the number of likely candidates before running that check.
/**
* Identify all relationships for a given model
*
* #param object $model Model
* #param string $heritage A flag that indicates whether parent and/or child relationships should be included
* #return array
*/
public function getAllRelations(\Illuminate\Database\Eloquent\Model $model = null, $heritage = 'all')
{
$model = $model ?: $this;
$modelName = get_class($model);
$types = ['children' => 'Has', 'parents' => 'Belongs', 'all' => ''];
$heritage = in_array($heritage, array_keys($types)) ? $heritage : 'all';
if (\Illuminate\Support\Facades\Cache::has($modelName."_{$heritage}_relations")) {
return \Illuminate\Support\Facades\Cache::get($modelName."_{$heritage}_relations");
}
$reflectionClass = new \ReflectionClass($model);
$traits = $reflectionClass->getTraits(); // Use this to omit trait methods
$traitMethodNames = [];
foreach ($traits as $name => $trait) {
$traitMethods = $trait->getMethods();
foreach ($traitMethods as $traitMethod) {
$traitMethodNames[] = $traitMethod->getName();
}
}
// Checking the return value actually requires executing the method. So use this to avoid infinite recursion.
$currentMethod = collect(explode('::', __METHOD__))->last();
$filter = $types[$heritage];
$methods = $reflectionClass->getMethods(\ReflectionMethod::IS_PUBLIC); // The method must be public
$methods = collect($methods)->filter(function ($method) use ($modelName, $traitMethodNames, $currentMethod) {
$methodName = $method->getName();
if (!in_array($methodName, $traitMethodNames) //The method must not originate in a trait
&& strpos($methodName, '__') !== 0 //It must not be a magic method
&& $method->class === $modelName //It must be in the self scope and not inherited
&& !$method->isStatic() //It must be in the this scope and not static
&& $methodName != $currentMethod //It must not be an override of this one
) {
$parameters = (new \ReflectionMethod($modelName, $methodName))->getParameters();
return collect($parameters)->filter(function ($parameter) {
return !$parameter->isOptional(); // The method must have no required parameters
})->isEmpty(); // If required parameters exist, this will be false and omit this method
}
return false;
})->mapWithKeys(function ($method) use ($model, $filter) {
$methodName = $method->getName();
$relation = $model->$methodName(); //Must return a Relation child. This is why we only want to do this once
if (is_subclass_of($relation, \Illuminate\Database\Eloquent\Relations\Relation::class)) {
$type = (new \ReflectionClass($relation))->getShortName(); //If relation is of the desired heritage
if (!$filter || strpos($type, $filter) === 0) {
return [$methodName => get_class($relation->getRelated())]; // ['relationName'=>'relatedModelClass']
}
}
return false; // Remove elements reflecting methods that do not have the desired return type
})->toArray();
\Illuminate\Support\Facades\Cache::forever($modelName."_{$heritage}_relations", $methods);
return $methods;
}
I have the same needs on my project. My solution is using get_class function to check type of relation. example:
$invoice = App\Models\Invoice::with('customer', 'products', 'invoiceProducts', 'invoiceProduct')->latest()->first();
foreach ($invoice->getRelations() as $relation => $items) {
$model = get_class($invoice->{$relation}());
$type = explode('\\', $model);
$type = $type[count($type) - 1];
$relations[] = ['name' => $relation, 'type' => $type];
}
dd($relations);
example result:
array:4 [▼
0 => array:2 [▼
"name" => "customer"
"type" => "BelongsTo"
]
1 => array:2 [▼
"name" => "products"
"type" => "BelongsToMany"
]
2 => array:2 [▼
"name" => "invoiceProducts"
"type" => "HasMany"
]
3 => array:2 [▼
"name" => "invoiceProduct"
"type" => "HasOne"
]
]
I need it for duplicate an model item including the relation
composer require adideas/laravel-get-relationship-eloquent-model
https://packagist.org/packages/adideas/laravel-get-relationship-eloquent-model
Laravel get relationship all eloquent models!
You don't need to know the names of the methods in the model to do this. Having one or many Eloquent models, thanks to this package, you can get all of its relationships and their type at runtime
I know its bit late, but I have been visiting this question multiple times so thought to share my observations to help those who visits this question in future.
Here is the method i used to extract the relationships from an eloquent model class.
/**
*
* Returns all the relationship methods defined
* in the provided model class with related
* model class and relation function name
*
* #param string $modelClass exampe: App\Models\Post
* #return array $relattions array containing information about relationships
*/
protected function getModelRelationshipMethods(string $modelClass)
{
//can define this at class level
$relationshipMethods = [
'hasMany',
'hasOne',
'belongsTo',
'belongsToMany',
];
$reflector = new ReflectionClass($modelClass);
$path = $reflector->getFileName();
//lines of the file
$lines = file($path);
$methods = $reflector->getMethods();
$relations = [];
foreach ($methods as $method) {
//if its a concrete class method
if ($method->class == $modelClass) {
$start = $method->getStartLine();
$end = $method->getEndLine();
//loop through lines of the method
for($i = $start-1; $i<=$end-1; $i++) {
// look for text between -> and ( assuming that its on one line
preg_match('~\->(.*?)\(~', $lines[$i], $matches);
// if there is a match
if (count($matches)) {
//loop to check if the found text is in relationshipMethods list
foreach ($matches as $match) {
// if so add it to the output array
if (in_array($match, $relationshipMethods)) {
$relations[] = [
//function name of the relation definition
'method_name' => $method->name,
//type of relation
'relation' => $match,
//related Class name
'related' => (preg_match('/'.$match.'\((.*?),/', $lines[$i], $related) == 1) ? $related[1] : null,
];
}
}
}
}
}
}
return $relations;
}
If you dd() or dump() the returned $relations for the App/Post model, The output will be something like this
^ array:3 [
0 => array:3 [
"method_name" => "user"
"relation" => "belongsTo"
"related" => "User::class"
]
1 => array:3 [
"method_name" => "tag"
"relation" => "belongsToMany"
"related" => "Tag::class"
]
2 => array:3 [
"method_name" => "comments"
"relation" => "hasMany"
"related" => "Comment::class"
]
]

Categories