Laravel Eloquent method updateOrCreate exceeds execution time when updating - php

So the problem is that when I try to update my entity it finds it updates it but gets stuck in a loop probably and doesn't exit. When I check the database, even before the 60 seconds of execution time that I have expires, the values that I have changed are updated.
If i constantly refresh (and here is where it gets crazy) the updated at values for other lectures starts to change every second while it executes this loop.
When creating (not finding the id on the condition It creates it without a problem)
I have Lectures which looks like this:
class Lecture extends Model
{
use Searchable;
use SoftDeletes;
protected $primaryKey = 'id';
protected $touches = ['themes', 'educationTypes', 'subjects'];
protected $fillable= [
'name', 'description', 'user_id', 'field_id', 'approved'
];
public static function boot()
{
parent::boot();
static::saved(function ($model) {
$model->themes->filter(function ($item) {
return $item->shouldBeSearchable();
})->searchable();
});
}
public function user(){
return $this->belongsTo('App\User')->with('companies');
}
public function geographies(){
return $this->belongsToMany('App\Geography');
}
public function educationTypes(){
return $this->belongsToMany('App\EducationType', 'lecture_education_type')->withTimestamps();;
}
public function themes(){
return $this->belongsToMany('App\Theme','lecture_theme', 'lecture_id', 'theme_id')->withTimestamps();;
}
public function subjects(){
return $this->belongsToMany('App\Subject', 'lecture_subject')->withTimestamps();;
}
public function cases(){
return $this->belongsToMany(
'App\CompanyCase' ,
'case_company_lecture',
'lecture_id',
'case_id',
'id',
'id')->withTimestamps();
}
public function companies(){
return $this->belongsToMany(
'App\Company' ,
'case_company_lecture',
'lecture_id',
'company_id',
'id',
'id'
);
}
public function field(){
return $this->belongsTo('App\Field');
}
public function toSearchableArray()
{
$this->themes;
$this->user;
$this->educationTypes;
$this->subjects;
$this->geography;
return $this->toArray();
}
}
This is the controller:
public function storeLecture(Request $request) {
$lecture_id = $request->get('lecture_id');
// It gets stuck between the comments
$lecture = Lecture::updateOrCreate(['id' => $lecture_id],
[
'name'=> request('name'),
'description'=> request('description'),
'user_id'=> request('user_id')]
);
// and doesn't update the themes, edu types subjects and etc.
$company_id = $request->get('company_id');
$company = Company::find(request('company_id'));
$lecture->companies()->sync([$company->id]);
$eduTypes= $request->get('education_types');
$themes= $request->get('themes');
$subjects = $request->get('subjects');
$geographies = $request->get('geographies');
$lecture->themes()->sync($themes);
$lecture->educationTypes()->sync($eduTypes);
$lecture->subjects()->sync($subjects);
$lecture->geographies()->sync($geographies);
$n1 = new Notification();
$n1->send(request('user_id'), 1, 'new_lecture', $lecture->id);
$user = User::where('id', $request->id)->first();
$user_with_companies = $user->load('companies');
$slug = $user_with_companies->companies->first()->slug;
return response(['success' => true]);
}
This is the frontend method sending the request (in between I have a middleware checking if the user is admin (possible to create a lecture) based on the this.selectedExpert.id, which doesn't interfere).
createUpdateLecture() {
const url = `${window.location.origin}/lecture/create/${
this.selectedExpert.id
}`;
this.$http
.post(url, {
education_types: this.allEducationTypes
.filter(el => el.checked)
.map(a => a.id),
themes: this.allThemes.filter(el => el.checked).map(a => a.id),
geographies: this.allGeographies
.filter(el => el.checked)
.map(a => a.id),
subjects: this.allSubjects.filter(el => el.checked).map(a => a.id),
name: this.lecture.name,
description: this.lecture.description,
user_id: this.selectedExpert.id,
company_id: this.company.id,
lecture_id: this.lecture.id
})
.then(res => {
console.log(res);
this.$parent.showLectureCreateModal = false;
// window.location.reload();
});
}
As I can see what is happening I probably use the method really badly but I just want to understand it better for further usage.

After a few days of researching and testing it turns out that it is not the updateOrCreate method causing the problem because I tried with two different functions for creating and updating and the update function was still having the same problem.
The problem is created from Algolia which is used for searching based on different fields in the platform. Fx. in Themes
class Theme extends Model
{
use SoftDeletes;
use Searchable;
protected $touches = ['lectures'];
public function lectures(){
return $this->belongsToMany('App\Lecture');
}
public function toSearchableArray()
{
$this->lectures;
return $this->toArray();
}
}
Removing the searchable from the models did the trick!

Related

Laravel find() does not get data from the DB

I have a resource Controller with this index method like this:
public function index()
{
$args = [];
$args = array_merge($args, $this->data_creator(35, 12, 'book'));
$args = array_merge($args, $this->data_creator(37, 12, 'kit'));
$args = array_merge($args, $this->data_creator(38, 12, 'game'));
$args['menu_links'] = [
'books' => route('shopping-products.category', Category::find(25)->slug),
'videos' => route('shopping-products.category', Category::find(24)->slug),
'kits' => route('shopping-products.category', Category::find(23)->slug),
'games' => route('shopping-products.category', Category::find(22)->slug),
];
return view('frontend.shop.products.index', $args);
}
But it returns this error:
Trying to get property 'slug' of non-object
And when I dd(Category::find(25), Category::find(24), Category::find(23), Category::find(22)); I get NULL results.
Meaning that it can not find data with specified ids.
However there are 25 records stored at the categories table:
So what is going wrong here? How can I fix this issue?
I would really appreciate any idea or suggestion from you guys...
Thanks in advance.
Here is Category.php Model:
class Category extends Model
{
use Sluggable, SoftDeletes;
protected $table = 'categories';
protected $primaryKey = 'cat_id';
protected $guarded = [];
/**
* Return the sluggable configuration array for this model.
*
* #return array
*/
public function sluggable()
{
return [
'slug' => [
'source' => 'cat_name'
]
];
}
public function path()
{
return "/products/categories/$this->slug";
}
public function children()
{
return $this->hasMany(Category::class, 'cat_parent_id', 'cat_id');
}
public function parents()
{
return $this->hasMany(Category::class, 'cat_id', 'cat_parent_id');
}
public function products()
{
return $this->belongsToMany(Product::class, 'category_products', 'ctp_cat_id', 'ctp_prd_id');
}
public function news()
{
return $this->belongsToMany(News::class, 'category_news', 'ctn_cat_id', 'ctn_nws_id');
}
public function galleries()
{
return $this->belongsToMany(Gallery::class, 'category_galleries', 'ctg_cat_id', 'ctg_gly_id');
}
public function uploaded()
{
return $this->hasMany(UploadedFile::class, 'upf_object_id', 'cat_id')->where('upf_object_type_id', '=', '107');
}
public function articles()
{
return $this->belongsToMany(Article::class, 'article_category', 'act_cat_id', 'act_art_id');
}
public function olympiadExam()
{
return $this->belongsToMany(OlympiadExam::class, 'olympiads_exams_categories', 'oec_ole_id', 'oec_cat_id');
}
public function olympiadExamQuestion()
{
return $this->belongsToMany(OlympiadExamQuestion::class, 'olympiads_exams_questions_categories', 'oes_cat_id', 'oes_oeq_id')->orderBy('oeq_number', 'asc');
}
public function attr_attributes()
{
return $this->hasMany(CategoryAttribute::class, 'category_id', 'cat_id');
} //
public function attr_product()
{
return $this->hasMany(Product::class, 'prd_cat_att_id', 'cat_id');
} //
public function couponRelation()
{
return $this->hasMany(couponRelation::class, 'object_id', 'cat_id')->where('object_type', 'product_category');
}
public function magazines()
{
return $this->belongsToMany(Magazine::class, 'category_magazine', 'category_id', 'magazine_id');
}
}
And when I do: dd(Category::where('cat_id', 25), Category::where('cat_id', 24), Category::where('cat_id', 23), Category::where('cat_id', 22)); I get this as result:
The problem is because you are using SoftDeletes so soft deleted models will automatically be excluded from query results. In your case, look like Category with id 22, 23, 24, 25 are soft deleted. To get it, you need to use withTrashed() as mentioned in the doc. For example:
Category::withTrashed()->find(22)->slug
per an answer above: if you are using soft deletes you need to add
Category::withTrashed()
However, you can wrap the command in an optional() helper function.
optional(Category::find(22))->slug
// if you are using soft delete
optional( Category::withTrashed()->find(22) )->slug
this will return null if 22 does not exist instead of throwing an exception error.

Display table data without user auth

I'm studying event message board. I can display table data by every Users own post. however I would like to display All post too. I wrote this as $tasksall but it didn't work. Could someone teach me what is wrong?
AController.php
public function index()
{
$tasks = Auth::user()
->tasks()
->orderBy('is_complete')
->orderByDesc('created_at')
->paginate(5);
$tasksall =
->tasks()
->orderBy('is_complete')
->orderByDesc('created_at')
->paginate(5);
return view('tasks', [
'tasks' => $tasks, 'tasksall' => $tasksall
]);
}
Task.php (model)
class Task extends Model
{
protected $casts = [
'is_complete' => 'boolean',
];
protected $fillable = [
'title',
'is_complete',
];
public function user()
{
return $this->belongsTo(User::class);
}
}
AController.php I add this code
public function person()
{
return $this->belongsTo('App\Models\Task');
}
public function getData()
{
return $this->id . ':'/ $this->person->name.')';
}
index.blade.php I add this code
{{ $task2->getData() }}
You can just write a query to get all the task using eloquent to get all the tasks.
$tasksall = Task::all();
Have a look at this link.
Also for you case I think the problem is you are getting task from the User model so you $task will contain only task related to that particular user as you have a belongsTo relation of task with user.
For Your case to get name of User from task.
//Task model
class Task {
public function user()
{
return $this->belongsTo(User::class, 'user_id');
}
}
Then you can query like this in your controller.
$task = Task::find($id);
$name = $task->user->name;

Laravel on delete objects call model event - deleted

Im trying to call model event - deleted. When Im deleting a video, I also want delete all comments which are associated with video, that is working fine. But I have also feeds table and when Im deleting video I want also delete all comments and comments feeds. Now when Im deleting video, I delete - video, video comments, video feed, but I need delete also video comment feeds.
The question is how I can make it possible to delete also comments feeds when Im deleting video?
Check VideoController.php destroy function
Video.php - Model
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
use Cviebrock\EloquentSluggable\Sluggable;
class Video extends Model
{
protected $table = 'videos';
public $timestamps = false;
use Sluggable, RecordsFeed;
public static function boot()
{
parent::boot();
}
public function sluggable()
{
return [
'slug' => [
'source' => 'title'
]
];
}
public function comments()
{
return $this->hasMany('App\VideoComment', 'videoid', 'id');
}
public function member()
{
return $this->belongsTo('App\Member', 'userid', 'member_id');
}
}
VideoComments.php - Model
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class VideoComment extends Model
{
protected $table = 'videos_comments';
public $timestamps = false;
protected $fillable = [
'text', 'userid', 'date'
];
use RecordsFeed;
public function videos() {
return $this->belongsTo('App\Video', 'id', 'videoid');
}
public function member() {
return $this->belongsTo('App\Member', 'userid', 'member_id');
}
}
RecordsFeed.php
<?php
namespace App;
use App\Libraries\Portal;
trait RecordsFeed
{
protected static function bootRecordsFeed() {
static::created(function($model) {
$model->recordFeed('created');
});
static::deleted(function($model) {
$model->deleteFeed('deleted');
});
}
public function feeds() {
return $this->morphMany( Feed::class, 'feedable' );
}
protected function recordFeed($event) {
$this->feeds()->create([
'user_id' => (new Portal)->getMemberID(),
'type' => $event.'_'.strtolower(class_basename($this))
]);
}
protected function deleteFeed($event) {
$this->feeds()->delete();
}
}
VideoController.php destroy function
public function destroy($id)
{
$video = Video::findOrFail($id);
$video->comments()->delete();
$video->delete();
Session::flash('success', 'Video deleted');
return redirect()->route('video.index');
}
Why do you not use on delete cascade at the creation of your table ?
In your migration, when you create your foreign key, just specify the on delete action like this :
$table->foreign('your_key')->references('id')->on('your_table')->onDelete('cascade');
It will automatically delete rows which are associated to the parent object.
You can find more details in Laravel documentation just here.
You don't have do anything code, you can set in database and set delete on cascade with relationshiop.

Laravel - Eloquent attach() not working for models

TL;DR
Trying to get three models to interact using eloquent for a rest api.
User - belongsToMany(pulls)
Pull - belongsToMany(user) && belongsToMany(boxes)
Box - belongsToMany(pulls)
The pull_user table is working perfectly, I can just attach a user after I save a pull. Saving a box works fine but the attach doesn't work/enter anything into the pivot table (I get no errors though).
The Problem
I can't get a pivot table that associates two of my models together to attach() after a save. I have the three models listed above, the pivot is working for pull_user but not for pull_box even though the save for box is working perfectly. I am able to save a box without an error but the association just never occurs (no error).
The Code
pull_box.php
class PullBox extends Migration
{
public function up()
{
Schema::create('pull_box', function (Blueprint $table) {
$table->increments('id');
$table->timestamps();
$table->integer('pull_id');
$table->integer('box_id');
});
}
public function down()
{
Schema::dropIfExists('pull_box');
}
}
Pull.php
class Pull extends Model
{
protected $fillable = ['from', 'to', 'runit_id', 'start_time', 'end_time', 'box_count', 'pull_status', 'audit_status', 'status', 'total_quantity', 'accuracy'];
public function users(){
return $this->belongsToMany('App\User');
}
public function boxes(){
return $this->belongsToMany('App\Box');
}
}
Box.php
class Box extends Model
{
protected $fillable = ['user_id','from', 'to', 'runit_id', 'start_time', 'end_time', 'pull_id', 'total_quantity', 'status', 'accuracy'];
public function pulls(){
return $this->belongsToMany('App\Pull');
}
}
BoxController.php
public function store(Request $request)
{
$this->validate($request, [
'user_id' => 'required|integer',
...
]);
$user_id = $request->input('user_id');
...
$box = new Box([
'user_id' => $user_id,
...
]);
$pull = Pull::whereId($pull_id)->first();
if($box->save()){
$pull->boxes()->attach($box->id);
$box->view_box = [
'href' => 'api/v1/box/' . $box->id,
'method' => 'GET'
];
$message = [
'msg' => 'Box created',
'box' => $box,
'pull' => $pull_id
];
return response()->json($message, 201);
}
$response = [
'msg' => 'Box creation error, contact supervisor',
];
return response()->json($response, 404);
}
The Solution
I need to know how I can get this association working. I am going to need to add a new layer in under the pull for Item, but I don't want to move one before I solve this. I think that my problem has to stem from a syntactical/logical error on my part but I can't see it. There are a bunch of questions on SO that are very close to giving me a solution, but after reading them I wasn't able to solve my problem.
Any help is appreciated.
Try renaming your pull_box table to box_pull, pivot tables on laravel must be in alphabetical order. If you want to use custom name on pivot table you have to extends your pivot, for example:
<?php
namespace App;
use Illuminate\Database\Eloquent\Relations\Pivot;
class PullBox extends Pivot
{
protected $table = 'pull_box';
}
And your many to many relationships:
class Pull extends Model
{
protected $fillable = ['from', 'to', 'runit_id', 'start_time', 'end_time', 'box_count', 'pull_status', 'audit_status', 'status', 'total_quantity', 'accuracy'];
public function users(){
return $this->belongsToMany('App\User');
}
public function boxes(){
return $this->belongsToMany('App\Box')->using('App\PullBox');
}
}
class Box extends Model
{
protected $fillable = ['user_id','from', 'to', 'runit_id', 'start_time', 'end_time', 'pull_id', 'total_quantity', 'status', 'accuracy'];
public function pulls(){
return $this->belongsToMany('App\Pull')->using('App\PullBox');
}
}

Laravel 5.2 - Using SetIdAttribute() Mutator To Set Other Value

I am currently creating a blog where each Post row in my database will have a unique hash attribute that is based of the post's id (incrementing, always unique).
This my Post model
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
use Hashids;
class Post extends Model
{
public function setTitleAttribute($value)
{
$this->attributes['title'] = $value;
if (! $this->exists) {
$this->attributes['slug'] = str_slug($value);
}
}
public function setIdAttribute($value) {
$this->attributes['id'] = $value;
$this->attributes['hash'] = Hashids::encode($value);
}
}
When I run this factory
$factory->define(App\Post::class, function (Faker\Generator $faker) {
return [
'title' => $faker->sentence(mt_rand(3, 10)),
'content' => join("\n\n", $faker->paragraphs(mt_rand(3, 6))),
'author' => $faker->name,
'category' => rand(1, 20),
];
});
The setIdAttribute($value) function is getting called, but my hash attribute is not being set. I am not sure if it is getting overwritten or what.
If I move the line
$this->attributes['hash'] = Hashids::encode($value);
to the function
public function setTitleAttribute($value)
and encode the title attribute it works fine, but I want to encode the 'id' attribute. Any idea how I would do this?
You can add the following to your model:
/**
* Events
*/
public static function boot()
{
parent::boot();
static::created(function($model)
{
$model->hash = Hashids::encode($model->id);
$model->slug = str_slug($model->title);
}
}
It's likely setIdAttribute($value) isn't being called until after the insert runs because it doesn't know the ID until then.
The real issue is you can't set a hash of the id in the same query because the id isn't going to be known (assuming it's auto_incrementing) until after the insert.
Because of this, the best you can probably do here is fire some code on the model's saved event.
In that model, you can probably do something like...
public static function boot()
{
parent::boot();
static::flushEventListeners(); // Without this I think we have an infinite loop
static::saved(function($post) {
$post->hash = Hashids:encode($post->id);
$post->save();
});
}

Categories