laravel replicate() method and computed database column - laravel

I'm trying to copy a row using by replicate() method:
$doc = Doc::find(64618);
$newdoc = $doc->replicate();
$newdoc->Price= 9999;
$newdoc->save();
but this table contains several computed columns and I get an error that the data fields can not be updated.
How can I exclude these fields when using replicate()?

You can pass the columns that you want to excludes
$doc = Doc::find(64618);
$newdoc = $doc->replicate(['column1', 'column2']);
$newdoc->Price= 9999;
$newdoc->save();
Here is the source you will find out if u deep dive into the core.
/**
* Clone the model into a new, non-existing instance.
*
* #param array|null $except
* #return \Illuminate\Database\Eloquent\Model
*/
public function replicate(array $except = null)
{
$defaults = [
$this->getKeyName(),
$this->getCreatedAtColumn(),
$this->getUpdatedAtColumn(),
];
$except = $except ? array_unique(array_merge($except, $defaults)) : $defaults;
$attributes = Arr::except($this->attributes, $except);
$instance = new static;
$instance->setRawAttributes($attributes);
return $instance->setRelations($this->relations);
}

Related

laravel access a eloquent row data by index

hello i have the laravel model:
class User extends Authenticatable
{
use SoftDeletes;
use Notifiable;
/**
* The database table used by the model.
*
* #var string
*/
protected $table = 'users';
/**
* The database primary key value.
*
* #var string
*/
protected $primaryKey = 'id';
/**
* Attributes that should be mass-assignable.
*
* #var array
*/
protected $fillable = [
'name',
'family',
];
}
in controller i get a user by id like :
$user=User::where('id','=' ,'10')->first () ;
in blade i display the family value :
{{$user->family}}
i am using :
{{$user[3] }}
to get the family value .
can we get the value by index like 2 for name ,
3 for family instead to do it like this $user->name ,or $user->family ?
thanks
Well, I hope you know what you are doing. As you want to access the User object by index.
Here is the snippet for controller:-
$user = User::where('id','=' ,'10')->first();
$user = array_values($user->toArray());
Now the $user has an array and you can access in the blade file by index.
Note that:- This will create an issue when you add more fields or remove some fields.

Laravel Scout and TNTSearch search withTrashed

I've configured Laravel Scout and can use ::search() on my models.
The same models also use SoftDeletes. How can I combine the ::search() with withTrashed()?
The code below does not work.
MyModel::search($request->input('search'))->withTrashed()->paginate(10);
The below does work but does not include the trashed items.
MyModel::search($request->input('search'))->paginate(10);
Update 1
I found in the scout/ModelObserver that deleted items are made unsearchable. Which is a bummer; I wanted my users to be able to search through their trash.
Update 2
I tried using ::withoutSyncingToSearch, as suggested by #camelCase, which I had high hopes for, but this also didn't work.
$model = MyModel::withTrashed()->where('slug', $slug)->firstOrFail();
if ($model->deleted_at) {
$model->forceDelete();
} else {
MyModel::withoutSyncingToSearch(function () use ($model) {
$model->delete();
});
}
This caused an undefined offset when searching for a deleted item. By the way, I'm using the TNTSearch driver for Laravel Scout. I don't know if this is an error with TNTSearch or with Laravel Scout.
I've worked out a solution to your issue. I'll be submitting a pull request for Scout to hopefully get it merged with the official package.
This approach allows you to include soft deleted models in your search:
App\User::search('search query string')->withTrashed()->get();
To only show soft deleted models in your search:
App\User::search('search query string')->onlyTrashed()->get();
You need to modify 3 files:
Builder.php
In laravel\scout\src\Builder.php add the following:
/**
* Whether the search should include soft deleted models.
*
* #var boolean
*/
public $withTrashed = false;
/**
* Whether the search should only include soft deleted models.
*
* #var boolean
*/
public $onlyTrashed = false;
/**
* Specify the search should include soft deletes
*
* #return $this
*/
public function withTrashed()
{
$this->withTrashed = true;
return $this;
}
/**
* Specify the search should only include soft deletes
*
* #return $this
*/
public function onlyTrashed()
{
$this->onlyTrashed = true;
return $this;
}
/**
* Paginate the given query into a simple paginator.
*
* #param int $perPage
* #param string $pageName
* #param int|null $page
* #return \Illuminate\Contracts\Pagination\LengthAwarePaginator
*/
public function paginate($perPage = null, $pageName = 'page', $page = null)
{
$engine = $this->engine();
$page = $page ?: Paginator::resolveCurrentPage($pageName);
$perPage = $perPage ?: $this->model->getPerPage();
$results = Collection::make($engine->map(
$rawResults = $engine->paginate($this, $perPage, $page), $this->model, $this->withTrashed, $this->onlyTrashed
)); // $this->withTrashed, $this->onlyTrashed is new
$paginator = (new LengthAwarePaginator($results, $engine->getTotalCount($rawResults), $perPage, $page, [
'path' => Paginator::resolveCurrentPath(),
'pageName' => $pageName,
]));
return $paginator->appends('query', $this->query);
}
Engine.php
In laravel\scout\src\Engines\Engine.php modify the following:
/**
* Map the given results to instances of the given model.
*
* #param mixed $results
* #param \Illuminate\Database\Eloquent\Model $model
* #param boolean $withTrashed // New
* #return \Illuminate\Database\Eloquent\Collection
*/
abstract public function map($results, $model, $withTrashed, $onlyTrashed); // $withTrashed, $onlyTrashed is new
/**
* Get the results of the given query mapped onto models.
*
* #param \Laravel\Scout\Builder $builder
* #return \Illuminate\Database\Eloquent\Collection
*/
public function get(Builder $builder)
{
return Collection::make($this->map(
$this->search($builder), $builder->model, $builder->withTrashed, $builder->onlyTrashed // $builder->withTrashed, $builder->onlyTrashed is new
));
}
And finally, you just need to modify your relative search engine. I'm using Algolia, but the map method appears to be the same for TNTSearch.
AlgoliaEngine.php
In laravel\scout\src\Engines\AlgoliaEngine.php modify the map method to match the abstract class we modified above:
/**
* Map the given results to instances of the given model.
*
* #param mixed $results
* #param \Illuminate\Database\Eloquent\Model $model
* #param boolean $withTrashed // New
* #return \Illuminate\Database\Eloquent\Collection
*/
public function map($results, $model, $withTrashed, $onlyTrashed) // $withTrashed, $onlyTrashed is new
{
if (count($results['hits']) === 0) {
return Collection::make();
}
$keys = collect($results['hits'])
->pluck('objectID')->values()->all();
$modelQuery = $model->whereIn(
$model->getQualifiedKeyName(), $keys
);
if ($withTrashed) $modelQuery->withTrashed(); // This is where the query will include deleted items, if specified
if ($onlyTrashed) $modelQuery->onlyTrashed(); // This is where the query will only include deleted items, if specified
$models = $modelQuery->get()->keyBy($model->getKeyName());
return Collection::make($results['hits'])->map(function ($hit) use ($model, $models) {
$key = $hit['objectID'];
if (isset($models[$key])) {
return $models[$key];
}
})->filter();
}
TNTSearchEngine.php
/**
* Map the given results to instances of the given model.
*
* #param mixed $results
* #param \Illuminate\Database\Eloquent\Model $model
*
* #return Collection
*/
public function map($results, $model, $withTrashed, $onlyTrashed)
{
if (count($results['ids']) === 0) {
return Collection::make();
}
$keys = collect($results['ids'])->values()->all();
$model_query = $model->whereIn(
$model->getQualifiedKeyName(), $keys
);
if ($withTrashed) $model_query->withTrashed();
if ($onlyTrashed) $model_query->onlyTrashed();
$models = $model_query->get()->keyBy($model->getKeyName());
return collect($results['ids'])->map(function ($hit) use ($models) {
if (isset($models[$hit])) {
return $models[$hit];
}
})->filter();
}
Let me know how it works.
NOTE: This approach still requires you to manually pause syncing using the withoutSyncingToSearch method while deleting a model; otherwise the search criteria will be updated with unsearchable().
this is my solution.
// will return matched ids of my model instance
$searchResultIds = MyModel::search($request->input('search'))->raw()['ids'];
MyModel::whereId('id', $searchResultIds)->onlyTrashed()->get();

how to update data into table using in laravel

I want to update data into table if the record exits if not then create a new record.
if(!empty($request->meta_title)){
$meta = new \stdclass();
$meta = VendorMeta::firstOrNew(['vendor_id' => $id]);
$meta->meta_title = $data['meta_title'];
$meta->meta_desc = $data['meta_desc'];
$meta->meta_keyword = $data['meta_keyword'];
$meta->save();
}
But I am getting this error:
MassAssignmentException in Model.php line 445:vendor_id
You should define which model attributes you want to make mass assignable, so in your VendorMeta class add following code:
protected $fillable = ['vendor_id'];
You need to set the $fillable property on your model.
Take a look at the docs under Mass Assignment.
/**
* The attributes that are mass assignable.
*
* #var array
*/
protected $fillable = ['meta_title', 'meta_desc', 'meta_keyword'];

Hiding pivot attribute

I know you can hide an entire pivot table as such
protected $hidden = ['pivot'];
How do you hide a specific field within pivot table, like
protected $hidden = ['pivot.created_at'];
The above does not work from what I've tested
After so much trying and looking into source of Laravel Model, i finally got it achieved.
Please put following method at your Model.
/**
* Convert the model instance to an array.
*
* #return array
*/
public function toArray()
{
$attributes = $this->attributesToArray();
$attributes = array_merge($attributes, $this->relationsToArray());
unset($attributes['pivot']['created_at']);
return $attributes;
}
This solves the purpose.

Can't make a new Insertion - Laravel Eloquent ORM

I can't Insert into this table and this drives me crazy
This is the error Msg I get
var_export does not handle circular references
open: /var/www/frameworks/Scout/vendor/laravel/framework/src/Illuminate/Database/Connection.php
* #param Exception $e
* #param string $query
* #param array $bindings
* #return void
*/
protected function handleQueryException(\Exception $e, $query, $bindings)
{
$bindings = var_export($bindings, true);
$message = $e->getMessage()." (SQL: {$query}) (Bindings: {$bindings})";
Here is my Full Mode
<?php
namespace Models;
use Illuminate\Database\Eloquent\Collection;
class Student extends \Eloquent
{
/**
* The database table used by the model.
*
* #var string
*/
protected $table = 'students';
/**
* The rules used to validate new Entry.
*
* #var array
*/
protected $newValidationRules = array(
'studentCode' => 'unique:students,code|numeric|required',
'studentName' => 'required|min:2',
'dateOfBirth' => 'date',
'mobile' => 'numeric'
);
/**
* Relation with sessions (Many To Many Relation)
* We added with Created_at to the Pivot table as it indicates the attendance time
*/
public function sessions()
{
return $this->belongsToMany('Models\Session', 'student_session')->withPivot('created_at')->orderBy('created_at', 'ASC');
}
/**
* Get Student Subjects depending on attendance,
*/
public function subjects()
{
$sessions = $this->sessions()->groupBy('subject_id')->get();
$subjects = new Collection();
foreach ($sessions as $session) {
$subject = $session->subject;
$subject->setRelation('student', $this);
$subjects->add($subject);
}
return $subjects;
}
/**
* Insert New Subject
* #return Boolean
*/
public function insertNew()
{
$this->validator = \Validator::make(\Input::all(), $this->newValidationRules);
if ($this->validator->passes()) {
$this->name = \Input::get('studentName');
$this->code = \Input::get('studentCode');
if ($this->save()) {
return \Response::make("You have registered the subject successfully !");
} else {
return \Response::make('An Error happened ');
}
} else {
Return $this->validator->messages()->first();
}
}
}
I am just trying to insert a new row with three Columns (I call the insertNew function on instance of Student)
1- ID automatically incremented
2- Special Code
3- Name
And I got this above Msg
What's I have tried till now :
removing all relations between from this model and other models
that has this one in the relation
Removed the validation step in insertNew()
Removed the all Input class calls and used literal data instead.
note that I use similar Inserting function on other Models and it works flawlessly
Any Comments , Replies are appreciated :D
Solution
I solved it and the problem was that I am accessing the validator
$this->validator = \Validator::make(\Input::all(), $this->newValidationRules);
And it was because I forgot that
/**
* The validator object.
*
* #var Illuminate\Validation\Validator
*/
protected $validator;
I had a similar problem. But to me, changing this code:
if ($this->validator->passes()) {
$this->name = \Input::get('studentName');
$this->code = \Input::get('studentCode');"
to this:
if ($this->validator->passes()) {
$this->setAttribute ("name" , \Input::get('studentName'));
$this->setAttribute ("code" , \Input::get('studentCode'));"
solved it.

Resources