Laravel seeding with relationship Eloquent\Builder failing - laravel

I'm trying to seed my database with like this:
factory(App\User::class, 1)
->create()
->each(function($u) {
$role = factory(App\Role::class)->create();
$u->role()->save( $role );
});
and these are my model factories:
$factory->define(App\User::class, function (Faker\Generator $faker) {
return [
'name' => 'Jakub Kohout',
'email' => 'test#gmail.com',
'password' => bcrypt('Uchiha'),
'role_id' => 1
];
});
$factory->define(App\Role::class, function (Faker\Generator $faker) {
return [
'role_name' => 'Admin',
];
});
But I got this error:
Undefined property: Illuminate\Database\Eloquent\Builder::$orders
What am I doing wrong?

Sadly, the each function does not work with single elements. You have to create more than one Model to use the each function:
factory(App\User::class, 2)->create()->each(function($u) {
$role = factory(App\Role::class)->create();
$u->role()->save( $role );
});
Source
When only one element is created, the instance is returned directly instead of a collection.
This should work for your case:
$user = factory(App\User::class)->create();
$role = factory(App\Role::class)->create();
$user->role()->save( $role );

Related

Laravel broadcasting a Presence Channel using pusher unable to pass parameters

I've followed the documentation at https://laravel.com/docs/6.x/broadcasting step by step and make sure I copy and paste to be certain I don't make any mistake. I'm able to broadcast just fine and everything is working just fine. Because I'm unable to pass attributes, people in different roomId are counted as if they are all in the same room.
Here is the live example:
https://prayershub.com/worship/82 - 82 is the worship_id I would like to pass to:
Broadcast::channel('worship_presence_channel.{id}', function ($id) {
if(Auth()->check())
{
$profile = Auth()->user()->Profile;
$user = Auth()->user();
$data = [
'id' => $user->id,
'name' => $user->name,
'username' => $user->username,
'avatar' => config('app.storage').$profile->profile_image,
'url' => $profile->profile_url,
'favorite_bible_verse' => $profile->favorite_bible_verse
];
return $id;
}
});
From:
Echo.join(`worship_presence_channel.${id}`)
.here((users) => {
worshipers=users;
joinUser(worshipers);
$('.group-count').html(worshipers.length);
console.log(users);
})
.joining((user) => {
worshipers.push(user);
popupNewUser(user);
joinUser(worshipers);
$('.group-count').html(worshipers.length);
})
.leaving((user) => {
worshipers = worshipers.filter(function(obj) {
return (obj.id !== user.id);
});
popupLeaveUser(user);
joinUser(worshipers);
$('.group-count').html(worshipers.length);
});
I also have an event which seems to be unneccassary but it lools like this:
public function broadcastOn()
{
return new PresenceChannel('worship_presence_channel.58');
}
public function broadcastAs()
{
return 'worship_presence_channel.58';
}
Can anyone please, tell me what i'm doing wrong or if I get the whole thing just wrong. Please help!
I've figured it out, I've change the echo codes above to this:
Broadcast::channel('worship_presence_channel.{id}', function ($user, $id) {
if(Auth()->check())
{
$profile = $user->Profile;
$data = [
'id' => $user->id,
'name' => $user->name,
'username' => $user->username,
'avatar' => config('app.storage').$profile->profile_image,
'url' => $profile->profile_url,
'favorite_bible_verse' => $profile->favorite_bible_verse,
'worships_id' => $id
];
return $data;
}
});
I'm passing 2 parameters $user, $id and it works just as the doc said it would!!!

Laravel 6 Factory, can't access relationship methods after using state

Suppose I have a user factory to create user, for specific user I want to add more information, for this I've defined state inside factory, but if I use state, then I can not call related method of user after user creation suppose I should assign role to the user using assignRole($role).
Following is my UserFactory.php
$factory->define(User::class, function (Faker $faker) {
return [
'email' => $faker->unique()->safeEmail,
'email_verified_at' => now(),
'password' => '$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', // password
'remember_token' => Str::random(10),
'first_name' => $faker->name,
'last_name' => $faker->lastName,
'phone' => $faker->phoneNumber,
'password' => bcrypt('12345678'),
];
});
$factory->state(User::class, 'Admin', function (Faker $faker) {
$country = factory(Country::class)->create();
$province = factory(Province::class)->create();
static $afghanistanId = 1;
$admin= [
'about' => $faker->sentence($nbWords = 20, $variableNbWords = true),
'address' => $faker->address,
];
if($country->id == $afghanistanId) {
$admin['province_id'] = $province->id;
} else {
$admin['city'] = $faker->city;
}
return $admin;
});
Now if I use this factory like bellow:
$user = factory(User::class, $count)->state('Admin')->create();
$user->assignRole('Admin');
Following shows this error:
BadMethodCallException: Method Illuminate\Database\Eloquent\Collection::assignRole does not exist
By passing $count to the factory method $user = factory(User::class, $count)...->create(); you're creating multiple users and the return is a Collection like your Error says.
To assigne the 'Admin' role to each user you have to iterate them
$users = factory(User::class, $count)->state('Admin')->create();
foreach($users as $user)
$user->assignRole('Admin');

Returning laravel realtionship within json

I am currently doing this as title says like this:
$user = User::where('username', request('username'))->first();
$posts = [];
$comments = [];
foreach($user->posts as $post){
foreach($post->comments as $comment){
array_push($comments, [
'id' => $comment->id,
'body' => $comment->body,
'user' => $comment->user->only(['name', 'id']),
]);
}
array_push($posts, [
'title' => $post->title,
'id' => $post->id,
'body' => $post->body,
'comments' => $comments,
]);
}
return response()->json([
'user' => $user->only(['name', 'avatar', 'age']),
'posts' => $posts,
]);
Is there a shorter way of doing this like:
$user->only(['name', 'avatar', 'age'])->withPostsOnly(['id', 'title', 'body'])->withCommentsOnly(['id', 'body']);
I know there is a way to make methods inside models that return these parts of data and then to use it same as above but shorter.
But is there any way to use something like getNameAttribute($value) for relations so I can say:
$user->only(['id', 'name', 'age', 'posts']);
And in posts value I need to have all posts and relationship data like comments and users that are connected to comments.
So basically in User model:
public function posts() {
return $this->hasMany('App/Post')->only('id', 'title', 'body', 'comments');
}
And inside Post model:
public function comments() {
return $this->hasMany('App/Comment')->only('id', 'body', 'user');
}
And inside Comment model:
public function comments() {
return $this->belongsTo('App/User')->only('id', 'name');
}
Thanks
You are probably overcomplicating it to be honest.
$user = User::where('username', request('username'))->first();
$user->load(['posts.comments']);
return response()->json($user);
This is a simplified version maybe but still should indicate you can just load relationships on models.

Test the exist validator in Yii2 without database with mockeryBuilder()

I want to test my AR model without connect to database in Yii 2 so I use mockBuilder() but I dont know how can I pass the mock object to the model exist validator, for example:
class Comment extends ActiveRecord
{
public function rules()
{
return [
[['id', 'user_id', 'post_id'], 'comment'],
['comment', 'string',
'max' => 200
],
['user_id', 'exist',
'targetClass' => User::className(),
'targetAttribute' => 'id'
],
['post_id', 'exist',
'targetClass' => Post::className(),
'targetAttribute' => 'id'
]
];
}
}
class CommentTest extends TestCase
{
public function testValidateCorrectData()
{
$user = $this->getMockBuilder(User::className())
->setMethods(['find'])
->getMock();
$user->method('find')->willReturn(new User([
'id' => 1
]));
$post = $this->getMockBuilder(Post::className())
->setMethods(['find'])
->getMock();
$post->method('find')->willReturn(new Post([
'id' => 1
]));
// How can I pass to $user and $post to exist validator in Comment model?
$comment = new Comment([
'user_id' => 1,
'post_id' => 1,
'comment' => 'test...'
]);
expect_that($comment->validate());
}
}
ok, It's not a best code just I'd like to introduce what I want to do.
Yii2 ExistValidator uses ActiveQuery::exists() for check existence and you should replace generated validator to mockobject where the method createQuery returns mockobject of ActiveQuery where ::exists() return something you want (true/false) e.g.
$activeQueryMock = $this->getMockBuilder(ActiveQuery::className())
->disableOriginalConstructor()
->setMethods(['exists'])
->getMock();
$activeQueryMock->expects($this->any())
->method('exists')
->willReturn($value); //Your value here true/false
$model = $this->getMockBuilder(Comment::className())
->setMethods(['getActiveValidators'])
->getMock();
$model->expects($this->any())
->method('getActiveValidators')
->willReturnCallback(function () use ($activeQueryMock) {
$validators = (new Comment())->activeValidators;
foreach ($validators as $key => $validator) {
if (($validator instanceof ExistValidator) && ($validator->attributes = ['user_id'])) {
$mock = $this->getMockBuilder(ExistValidator::className())
->setConstructorArgs(['config' => get_object_vars($validator)])
->setMethods(['createQuery'])
->getMock();
$mock->expects($this->any())
->method('createQuery')
->willReturn($activeQueryMock);
$validators[$key] = $mock;
break;
}
}
return $validators;
});
$model->validate();

Faker with Laravel taking appending data from another factory

$factory->define(App\Client::class, function (Faker\Generator $faker) {
static $password;
$company_name = $faker->Company;
return [
'name' => $company_name,
'short_name' => substr($company_name, 0, 3),
'email' => $faker->unique()->safeEmail,
'password' => $password ?: $password = bcrypt('secret'),
'remember_token' => str_random(10),
];
});
$factory->define(App\Campaign::class, function (Faker\Generator $faker) {
static $password;
return [
'unique_id' => $faker->numerify('ABC###'),
'client' => function() {
return factory('App\Client')->create()->id;
}
];
});
I am generating come clients and campaigns. One client can have many campaigns
How do I take the short_name from the Company and pass it to a the campaign class so I can append it to a random string to create a unique id in the client?
You're almost there. You don't need to use an anonymous function in the campaign class, you can just reference the factory directly. Use a variable inside the Campaign factory and just reference whatever values you need.
$factory->define(App\Campaign::class, function (Faker\Generator $faker) {
$client = factory(App\Client::class)->create();
return [
'unique_id' => $faker->numerify('ABC###') . $client->short_name,
'client' => $client->id
];
});

Resources