Laravel fills attributes in model in migration even though they are not fillable - laravel

I have two entities:
Person, which has workspace_id column
PersonRevision, which does not have workspace_id column
In a migration I have this:
$persons = Person::all();
foreach ($persons as $person) {
$revision = new PersonRevision;
$revision->fill($person->getAttributes());
$revision->save();
}
PersonRevision does not have workspace_id in fillable property. But for some reason I got an error: column "workspace_id" of relation "person_revisions" does not exist.
Besides, if I run the migration second time (by just typing php artisan migrate second time) it works fine.
What could be the reason? I know I can manually list attributes which need to be filled, or use array_except but this is inconvenient and does not answer why that happens.

Could you check what the actual sql statement is that's executed? To me it seems like you have a database which does not contain workspace_id yet, but your model does expect it to be there. Even when not filling the workspace_id the query generated from your eloquent model will probably try to update it with the value null, which it can't do if it's not there.
Because of issues like this it's best not to use eloquent models in database migrations, instead use raw sql.

Laravel SeedCommand handle() function runs with Model::ungarded() which forces all props to be fillable. When unguarded is true it auto fills regardless of fillable array.
public function isFillable($key)
{
if (static::$unguarded) {
return true;
}
[...]
}
Simply add this atop your seeder file Model::reguard();

Related

How to set a value in a null JSON column with Laravel's Eloquent?

I'm using Laravel 7.
I want to update a JSON column using Laravel's Eloquent. The problem is that if the value of the column is null the column won't be updated.
This is how the code looks like:
Model::update(['jsonColumnName->jsonColumnKey' => 'value']);
This is the SQL that it would generate:
UPDATE model
SET jsonColumnName = JSON_SET(jsonColumnName, '$.jsonColumnKey', 'value');
According to the documentation of JSON_SET, it will take the first parameter as the JSON document that it will modify. In this case, that value would be null since jsonColumnName is currently null. Because of that it returns null since it has nothing to modify and it ends doing nothing.
If I manually set the value of the column to {} and run the showed code, it works. But I guess that you are not expected to do that (right?).
You should make new alter table migration and change json column to have default value {}.
First you need to check if there is already installed dbal with
composer require doctrine/dbal
Then make new migration with code in up() method:
Schema::table('table_name', function (Blueprint $table) {
$table->json('column_name')->nullable()->default(null)->change();
});
Don't forget to backup database before work on it.
With NULL value you can also check if that field is empty.
Another way, on framework level is to set logic about this issue into model's observer.
For example:
public function saving(EntityModel $entityModel)
{
if (is_null($entityModel->json_column)) {
$entityModel->json_column = '{}';
}
}

How to use create method with variables in Laravel

How can i define create method with variables and model in laravel? Like this:
public function createMethodName($variable, $variable2)
{
\App\ModelName::create([...]);
}
You have to just insert the passed variables in the array like:
// include model at the top of your file:
use \App\Models\ModelName;
public function createMethodName($variable1, $variable2)
{
$my_stuff = ModelName::create([
‘something’ => $variable1,
‘something_else’ => $variable2,
]);
}
Also, do not forget to make some of your Model fields fillable (fields which are included in the array) in your Model, if you want to save values to the table with using create(). Check the Reference about this:
https://laravel.com/docs/8.x/eloquent#mass-assignment
Short update:
Maybe I misunderstood your question a bit since according to your new comments under your question it is getting clearer that you maybe first would like to create a Table with Schema::create() method (which is a totally different part of the fun). Reference:
https://laravel.com/docs/8.x/migrations#creating-tables
Then after you created your Table, you can create a Model using artisan like:
php artisan make:model ModelName
Reference:
https://laravel.com/docs/8.x/eloquent#defining-models
and then you can use ModelName::create() method in your Controller as I gave it above to saving your data into your Table. So you should study these steps a bit more if it is the case.

Laravel - why is a Model's relations not being loaded?

I have a Laravel 5.3 site and I think maybe I have some weird things going on due to some actions happening in API controllers and some happening in the regular controllers.
Or maybe an issue where at some times I am dealing with a Model, and sometimes with an Eloquent Collection.
The issue is, I am trying to retrieve relations on a Model and am getting null.
For instance, I have course Model that relates to week Model.
In course Model I get week items as
public function weeks()
{
return $this->hasMany(Week::class, 'course_id');
}
In backend, these relations get sent in this way:
$course->load('weeks')
All is good.
But when course item gets deleted and I try and take action in the week controller as
static::deleting(function($course) {
$course->weeks->delete();
});
$course->weeks is null. At that time I see in the database that all is good and this course items does indeed have a week item related, but $course shows 0 relations.
So something odd is happening where $course->webinars is not grabbing the week items related.
Is there something that I am fundamentally doing wrong? Maybe it is because in the models I have these sorts of statements:
protected $table = 'Week';
Are these preventing the relations from being pulled? I always thought that is I had some function in a model that returns relations that those relations would always be available when I use syntax $course->weeks.
Ideas?
Thanks again,
You can simply setup migrations to automatically delete from weeks if you delete a course, provided you have foreign key relationship.
If you have a column course_id in weeks table then add this into your migration
$table->foreign('course_id')
->references('id')->on('courses')
->onDelete('cascade')
I think you can use Observers. In your AppServiceProvider, first register the observer.
public function boot()
{
Course::observe(CourseObserver::class);
}
Now, add an Observer class.
class CourseObserver
{
public function deleting(Course $course)
{
$course->weeks()->delete();
}
}

How to query from database when I have different foreign keys?

I am trying to query data from my database and pass the results to a view called events, the problem I have is that one of my queries will always return the same result because in the where condition the $events_id is the same always. Is there a better way to do the querying? A better logic?
This code is from my controller called EventController:
public function index()
{
$firm_id = DB::table('firms')->where('user_id', auth()->id())->value('id');
$events_id = DB::table('events')->where('firm_id', $firm_id)->value('id');
$events = DB::table('events')->where('firm_id', $firm_id)->get()->toArray();
$actual_events = DB::table('actual_events')->where('event_id', $events_id)->get()->toArray();
return view('events',['events' => $events,'actual_events' => $actual_events]);
}
Since the $events_id is the same every time, the $actual_events will only contain the first result.
The image I have uploaded shows the problem, my table's first three columns are fine. Starting from the fourth they contain repeated values:
As I guess, you need something like this:
$event_ids = DB::table('events')->where('firm_id', $firm_id)->pluck('id');
$actual_events = DB::table('actual_events')->whereIn('event_id', $event_ids)->get()->toArray();
or write about your problem in details and I will try to help you.
you just said that your tables have relation together.
in this case it's better you using the eloquent for that,
first you should type the relations in model of each table like this:
class User extends Authenticatable{
public function cities()
{
return $this->hasmany('App\City'); //you should type your relation
}
}
for relations you can use this link: laravel relationships
after that when you compact the $user variable to your view, you can use this syntax for getting the city value relation to this user: $user->cities;.

Laravel 5.3 - caching information schema queries

I use multiple domains for the same site structure, so almost all tables have the domain_id column. So, to NOT define the domain_id condition every time I am using the following approach (not sure if this is the best one)
I created BaseModel that other models are extending from and have this in boot method of that BaseModel
parent::boot();
static::addGlobalScope(new DomainScope());
and here is the apply method's content according to docs
if (Schema::hasColumn($model->getTable(), 'domain_id')) {
$builder->where('domain_id', '=', DOMAIN_ID);
}
This works great, however if I have e.g. 5 find queries on the same page, to the same table (just as an example) in debug panel I see 5 queries like this
select column_name from information_schema.columns where table_schema = 'my_db_name' and table_name = 'my_table_name'
Now, I perfectly understand that to check whether the column exists in the table it gets the information from information schema, but why it is making the same query for the same table over and over again. I would presume It should make one request and then cache it, and for subsequent requests just read from cache.
q1) Does laravel internally cache this query ? I am thinking maybe because debug is enabled, that is why each time its making a query ? but cant find any verification of this
q2) and if it does not cache, can I cache it manually. I checked the laravel docs for adding a cache, but the problem here that query is not done by me, so I cant figure out to simply use Cache::remember
Thanks
Well, I would suggest not to fire a query for checking if domain_id exists in the table every time. You can do this by simply adding a variable to your model that defines whether the model is a multi-domain model (has a domain_id column) or single domain model (no domain_id column) like so:
// In BaseModel.php
public $multiDomain = true; // override to false in your child model class if it's a single domain model
// In DomainScope apply method
if ($model->multiDomain)) {
$builder->where('domain_id', '=', DOMAIN_ID);
}
If you don't want to use the above, you can cache like so:
if(Cache::remember($model->getTable() . "_domain", $minutes, function() use($model) {
return Schema::hasColumn($model->getTable(), 'domain_id');
}) {
$builder->where('domain_id', '=', DOMAIN_ID);
}

Resources