Laravel Seeding Does not Fill in Fields - laravel

I have a database seed file:
class ContactTableSeeder extends Seeder {
public function run()
{
$contacts = array(
array(
'first_name' => 'Test',
'last_name' => 'Contact',
'email' => 'test.contact#emai.com',
'telephone_number' => '0111345685',
'address' => 'Address',
'city' => 'City',
'postcode' => 'postcode',
'position' => 'Director',
'account_id' => 1
)
);
foreach ($contacts as $contact) {
Contact::create($contact);
}
}
}
When I run php artisan migrate:refresh --seed it seeds the database and creates the relevant record in the contacts table, except that it does not fill the fields with any of the information in the seed array. I am using the exact same syntax for other tables and they work fine, and I've also checked each field thoroughly to make sure they match the database fields but no matter what I do it will not seed correctly.
Does anyone have any ideas?

I had this same problem but none of the above solutions worked for me. It turned out to be due to having a construct function in my model! After I removed this it worked fine!
public function __construct()
{
parent::__construct();
}
EDIT:
After further reading on this I discovered the issue is due to the fact that if you are going to include a constructor in your model must accept the attributes parameter and pass it to the parent. If you do this then the constructor does not break the DB seeding (and probably other things). I hope this saves someone else a headache.
public function __construct($attributes = array())
{
parent::__construct($attributes);
}

Turns out the issue was to do with relationships in my models.
For future visitors to this question: make sure to check all the functions in your models that define hasOne/hasMany/etc relationships. Read though the Eloquent docs for more.

Do you have $this->call("ContactTableSeeder") in your DatabaseSeeder class' run() function?
If you have ContactTableSeeder in it's own file, is the file named ContactTableSeeder.php exactly? If not it would fail to load according to the PSR-0 Standard.
These are my first thoughts.

Have you tried to replace the following lines:
foreach ($contacts as $contact) {
Contact::create($contact);
}
with
DB::table('contact')->insert($contacts);
assuming your table name is contact.
And also, make sure you have line like this
$this->call('ContactTableSeeder');
in your DatabaseSeeder class.

Related

Best way to handle a model relationship status/state when unit testing in Laravel 6?

I have the following unit test in Laravel 6.x, using SQLite:
<?php
/** #var \Illuminate\Database\Eloquent\Factory $factory */
use App\Entry;
use App\EntryStatus;
use Faker\Generator as Faker;
$factory->define(Entry::class, function (Faker $faker) {
return [
'user_id' => 1,
'caption' => $faker->sentence(10),
'entry_status_id' => EntryStatus::where('name', 'pending')->first()->id,
];
});
$factory->state(Entry::class, 'awaiting_payment', [
'entry_status' => EntryStatus::where('name', 'awaiting_payment')->first()->id,
]);
I have a test error of 'Trying to get property 'id' of non-object', which is for the following line:
'entry_status' => EntryStatus::where('name', 'awaiting_payment')->first()->id,
I have a few ideas on how to fix this error, but I'm wondering what the best practice here would be in terms of unit testing and 'The Laravel way'.
The way I have thought about this, I have an EntryStatus table which has static statues 'pending' => 0, 'awaiting_payment' => 1, 'paid' => 2 etc. And I create a relationship within my App\Entry model > App\EntryStatus. Ideas are as follows:
The original plan; for the unit tests, I need to seed the static EntryStatus table each time for each test. Looking at the docs, I would use something like setUp() > $this->artisan('db:seed'). But this feels like it would really slow the tests. Unless there is a way to seed the DB once before all tests are started.
Try and create a factory which creates static data ('pending' => 0, 'awaiting_payment' => 1) but I'd manually need to update the database seed and the factory each time to match which seems clunky.
Within the Laravel docs testing documents they have this example; $factory->state(App\User::class, 'delinquent', ['account_status' => 'delinquent',]). This makes me think I could just remove the relationship table EntryStatus completely and just use a string column in the Entry to represent the state. I think this would be the best solution, but I'm worried that there is a reason we use ids as statuses as I'd imagine that they are quicker in search queries. But if not, my statues will be fixed, and this seems like the most eloquent solution.
Another option would be to store the statues as integers anyway, keeping a note of what status is what integer, but again this doesn't seem correct.
The record is not created yet and its not a good idea to assign static relationships this way in factories.
You can use a factory create instead then assign the relationship inside the test case.
For example:
Entry factory
$factory->define(Entry::class, function (Faker $faker) {
return [
'user_id' => factory(User)->create()->id,
'caption' => $faker->sentence(10),
'entry_status_id' => factory(EntryStatus)->create()->id,
];
});
EntryStatus factory
$factory->define(EntryStatus::class, function (Faker $faker) {
return [
'name' => $faker->word,
'entry_status' => $faker->numberBetween(0, 2) //Its better start from 1 though
];
});
User factory
$factory->define(User::class, function (Faker $faker) {
return [
'name' => $faker->word
];
});
In your test case start linking things together (if you need to).
/**
* #test
*/
public function exampleTestCase()
{
$enteryStatus = factory(EntryStatus::class)->create(['entry_status' => 1]);
//create 6 entries
$entry = factory(Entry::class, 6)->create(['entry_status_id' => $enteryStatus->id]);
//TODO: assert something
}
You can check afterCreatingState
$factory
->state(EntryStatus::class, 'awaiting_payment', ['name' => 'awaiting_payment'])
->afterCreatingState(EntryStatus::class, 'awaiting_payment', function ($entryStatus, $faker) {
factory(Entry::class)->create([
'entry_status_id' => $entryStatus->id,
]);
});

How does Laravel generate SQL?

I'm brand new to Laravel and am working my way through the Laravel 6 from Scratch course over at Laracasts. The course is free but I can't afford a Laracasts membership so I can't ask questions there.
I've finished Section 6 of the course, Controller Techniques, and am having unexpected problems trying to extend the work we've done so far to add a few new features. The course has students build pages that let a user show a list of articles, look at an individual article, create and save a new article, and update and save an existing article. The course work envisioned a very simple article containing just an ID (auto-incremented in the database and not visible to the web user), a title, an excerpt and a body and I got all of the features working for that. Now I'm trying to add two new fields: an author name and a path to a picture illustrating the article. I've updated the migration, rolled back and rerun the migration to include the new fields and got no errors from that. (I also ran a migrate:free and got no errors from that.) I've also updated the forms used to create and update the articles and added validations for the new fields. However, when I go to execute the revised create code, it fails because the SQL is wrong.
The error message complains that the author field doesn't have a default, which is true, I didn't assign a default. However, I did give it a value on the form. What perplexes me most is the SQL that it has generated: the column list doesn't show the two new columns. And that's not all: the values list is missing apostrophes around any of the string/text values. (All of the columns are defined as string or text.)
As I said, I'm completely new to Laravel so I don't know how to persuade Laravel to add the two new columns to the Insert statement nor how to make it put apostrophes around the strings in the values list. That hasn't come up in the course and I'm not sure if it will come up later. I was hoping someone could tell me how to fix this. All of my functionality was working fine before I added the two new fields/columns.
Here is the error message:
SQLSTATE[HY000]: General error: 1364 Field 'author' doesn't have a default value (SQL: insert into `articles` (`title`, `excerpt`, `body`, `updated_at`, `created_at`) values (Today in Canada, The ideal winter-beater, This car is the ideal winter-beater for the tough Canadian climate. It is designed to get you from A to B in style and without breaking the bank., 2020-02-15 17:37:54, 2020-02-15 17:37:54))
Here is ArticlesController:
<?php
namespace App\Http\Controllers;
use App\Article;
use Illuminate\Http\Request;
class ArticlesController extends Controller
{
public function index()
{
$articles = Article::latest()->get();
return view ('articles.index', ['articles' => $articles]);
}
public function show(Article $article)
{
return view('articles.show', ['article' => $article]);
}
public function create()
{
return view('articles.create');
}
public function store()
{
//Stores a NEW article
Article::create($this->validateArticle());
return redirect('/articles');
}
public function edit(Article $article)
{
return view('articles.edit', ['article' => $article]);
}
public function update(Article $article)
{
//Updates an EXISTING article
$article->update($this->validateArticle());
return redirect('/articles/', $article->id);
}
public function validateArticle()
{
return request()->validate([
'title' => ['required', 'min:5', 'max:20'],
'author' => ['required', 'min:5', 'max:30'],
'photopath' => ['required', 'min:10', 'max:100'],
'excerpt' => ['required', 'min:10', 'max:50'],
'body' => ['required', 'min:50', 'max:500']
]);
}
public function destroy(Article $article)
{
//Display existing record with "Are you sure you want to delete this? Delete|Cancel" option
//If user chooses Delete, delete the record
//If user chooses Cancel, return to the list of articles
}
}
Is there anything else you need to see?
It may be possible because of you don't have defined that column in fillable property, to use mass assignment you have to specify that columns.
Try after adding that columns in fillable property.
Laravel mass assignment
Hope this helps :)

Simplify store controller method on laravel 5

This is my store method to save a post.
public function store(CreatePostRequest $request)
{
$post = new Post([
'title' => $request['title'],
'content' => Crypt::encrypt($request['content']),
'published_at' => Carbon::now()
]);
$post->user()->associate(Auth::user());
$newPost=Post::create($post->toArray());
$this->syncTags($newPost, $request['tag_list']);
return redirect('posts')->withMessage('Post Saved Successfully !!!');
}
In laracasts tutorial he is just doing a
Article::create($request->all());
I need to do the extra stuff like encrypt, but am i cluttering the method? could it be cleaner?
Do it in the Model. I use the set/get*Attribute() method to change stuff on the fly.
So you could use Article::create($request->all()); then in the model use the fillable array to only autofill what is allowed (such as title, content and published_at).
then use something like (in the model)
function setContentAttribute( $value ){
$this->attributes['content'] = Crypt::encrypt($value);
}
In fact you could also adapt this approach so that the published_at attribute is set to today, or even better use your database to provide now()s time.

Eloquent many to many to many record attach

I have two tables.
message - id, title, content
user - id, name
I have a pivot table.
message_user - message_id, user_id_sender, user_id_receiver
A message can be sent by 1-0 (sender can be null) users, defined with user_id_sender
A message can be received by 1-N users, defined with user_id_receiver
I am trying to define this in Eloquent.
The only solution I have is:
class Message
public function users()
{
return $this->belongsToMany(
'User',
'message_user',
'user_id_sender',
'user_id_receiver');
}
// Somewhere in the code
$message = $this->messageRepository->store($message);
$message->users()->attach($message->id, array(
'message_id' => $message->id,
'user_id_sender' => $userSender->id,
'user_id_receiver' => $userReceiver->id
));
This is the only solution I could think of, but it feels like a workaround. Is there a proper way to achieve this?
You could put into your Message model this:
public function betweenUsers($sender, $receiver) {
return $this->users()->attach($this->id, [
'message_id' => $this->id,
'user_id_sender' => $sender->id,
'user_id_receiver' => $receiver->id,
]);
}
Then you could somewhere in the code do $message->betweenUsers($userSender, $userReceiver);
I understand it is not a exactly what you wanted, but since you are really uncommon kind of pivot table, this is probably sufficient too.

Laravel: Eloquent how to update a model and related models in one go

Does anyone know if it is possitble to do the folowing:
Let's say we have a model called User and a model calledd BestFriend. The relation between the User and the best friend is 1:1.
I would like for these cases be able to do something like this, change my city and the city of my friend at the same time.
$me = User::find(1);
$me->update(array(
'city' => 'amsterdam',
'bestfriend.city' => 'amsterdam'
));
So basically I would like to know if Eloquent is smart enough to understand the relationship based on the array key 'bestfriend.city'.
Thanks in advance for any help!
Update:
Found the solution on the Laravel forums but im posting it here as well if someone else is looking for the same thing :)
In the model you add
// In your model...
public function setBestFriendArrayAttribute($values)
{
$this->bestfriend->update($values);
}
And then you can call it like this
$me->update(array(
'city' => 'amsterdam',
'BestFriendArray' => array(
'city' => 'amsterdam'
)
));
Works like a charm!
You don't need to set it on your model. You can do it on your controller like this.
$me = User::find(1)->bestFriend()->update(array(
'city' => 'amsterdam',
'bestfriend.city' => 'amsterdam'
));
I just modified your update a little bit.
Eloquent is pretty smart, but I don't believe it can do that. You would have to update User and BestFriend independently. But once you've done that, Eloquent does have methods for attaching the two.
$me = User::find(1);
$bff= BestFriend::find(1);
$me->city = 'amsterdam';
$bff->city = 'amsterdam';
$me->bestfriend()->associate($bff);
This is of course assuming your User model has a function that looks like...
public function bestfriend()
{
return $this->hasOne('BestFriend');
}
If you want to save same city name to both entities this will do the job:
$me = User::find(1);
$me->update(array(
'city' => 'amsterdam',
));
And in your model:
public function setCityAttribute($value)
{
$this->attributes['city'] = $value;
$this->bestfriend->city= $value;
$this->bestfriend->save();
}

Resources