Error when using Laravel 5.4 Seeder Function - laravel

Type error: Argument 1 passed to
Illuminate\Database\Eloquent\Relations\HasOneOrMany::save() must be an
instance of Illuminate\Database\Eloquent\Model, instance of
Illuminate\Database\Eloquent\Collection given
I am planning to generate Fake date for user with profile. I have two model: User & Profile. I have done the relationship with each others. But when i execute it and the problems came. Above is the error when i running Seeder.
This is the class that found bug.
public function run()
{
factory(App\User::class, 40)->create()->each(
function($user) {
$user->profile()->save(
factory(App\Profile::class, 1)->make()
);
}
);
}

I guess you are using Laravel 5.4.
The has been a modification in the factory() helper method. From the docs:
The factory Helper
Calling factory(User::class, 1)->make() or factory(User::class, 1)->create() will now return a collection with one item. Previously, this would return a single model. This method will only return a single model if the amount is not supplied.
Laravel 5.3 to 5.4 updgrade guide
That means that you should remove the 1 from your factory() call.
public function run(){
factory(App\User::class, 40)->create()->each(
function($user) {
$user->profile()->save(
factory(App\Profile::class)->make()
);
}
);}
Edit 1: You have to change this line:
factory(App\Profile::class, 1)->make()
To this
factory(App\Profile::class)->make()

Getting the relation $user->profile() by calling the method, will return the related models as a Collection. You can do $user->profile()->first() to get a single result of type Illuminate\Database\Eloquent\Model or just $user->profile without the ().

Related

Laravel nova resource extending/overriding the create method

I am developing a web admin panel using Laravel Nova.
I am having an issue since Nova is quite a new technology.
What I would like to do now is I would like to add a hidden field or extend or override the create method.
This is my scenario. Let's say I have a vacancy nova resource with the following field.
public function fields(Request $request)
{
return [
ID::make()->sortable(),
Text::make('Title')->sortable(),
Text::make('Salary')->sortable()
// I will have another field, called created_by
];
}
Very simple. What I like to do is I want to add a new field called created_by into the database. Then that field will be auto filled with the current logged user id ($request->user()->id).
How can I override or extend the create function of Nova? How can I achieve it?
I can use resource event, but how can I retrieve the logged in user in
the event?
What you're looking for is Resource Events.
From the docs:
All Nova operations use the typical save, delete, forceDelete, restore Eloquent methods you are familiar with. Therefore, it is easy to listen for model events triggered by Nova and react to them.
The easiest approach is to simply attach a model observer to a model:
If you don't feel like creating a new observable you could also create a boot method in your eloquent model as so:
public static function boot()
{
parent::boot();
static::creating(function ($vacancy) {
$vacancy->created_by = auth()->user()->id;
});
}
But please do note that these are a bit harder to track than observables, and you or a next developer in the future might be scratching their head, wondering how's the "created_at" property set.
In my opinion you should go for Observers. Observers will make you code more readable and trackable.
Here is how you can achieve the same with Laravel Observers.
AppServiceProvider.php
public function boot()
{
Nova::serving(function () {
Post::observe(PostObserver::class);
});
}
PostObserver.php
public function creating(Post $post)
{
$post->created_by = Auth::user()->id;
}
OR
You can simply hack a Nova field using withMeta.
Text::make('created_by')->withMeta([
'type' => 'hidden',
'value' => Auth::user()->id
])
You could also do that directly within your Nova resource. Every Nova resource has newModel() method which is called when resource loads fresh instance of your model from db. You can override it and put there your logic for setting any default values (you should always check if values already exist, and only set if they are null, which will only be the case when the model is being created for the first time, which is what you actually need):
public static function newModel()
{
$model = static::$model;
$instance = new $model;
if ($instance->created_by == null) {
$instance->created_by = auth()->user()->id;
}
return $instance;
}
a) Create an Observer class with following command:
php artisan make:observer -m "Post" PostObserver
b) Add following code in the PostObserver:
$post->created_by = Auth::user()->id;
c) Register PostObserver in AppServiceProvider.php
For detailed explanation: https://medium.com/vineeth-vijayan/how-to-add-a-new-field-in-laravel-nova-resource-87f79427d38c
Since Nova v3.0, there is a native Hidden field.
Usage:
Hidden::make('Created By', 'created_by')
->default(
function ($request) {
return $request->user()->id;
}),
Docs: https://nova.laravel.com/docs/3.0/resources/fields.html#hidden-field

laravel: how to pass a parameters to a function in model?

I'm using laravel 5.5 / php 7.2 /
for example, I have a function like this in a model:
public function featuredTopSight($count=8)
{
return $this->hasMany(Sight::class)
->wherePublish(1)
->latest()
->take($count);
}
and then, In view:
$sight->featuredTopSight(8);
But I got this error:
"htmlspecialchars() expects parameter 1 to be string, object given
but with $sight->featuredTopSight; I got right answer. but I can not pass the parameter.
In that function you're defining a relationship.
You can't call a relationship with arguments.
First you need to declare the relation, and then the query or method to retrieve results.
Your method should be something similar to:
public function sights()
{
return $this->hasMany(Sight::class);
}
public function getFeaturedTopSights($count = 8)
{
return $this->sights()->wherePublish(1)->latest()->take($count)->get();
}

How to improve Laravel code using with operator?

In my request to DB I use Laravel's models and function with like:
$user = User::with("city")->get();
Problem is that if to do the following in template:
{{$user->city()->name}}
It will work only then if user specified city, so then value exists in database table. Otherwise it returns an error:
How to get rid of superfluous checks like:
#if(isset($user->city()->name))
{{$user->city()->name}}
#endif
This is awfully!
When defining the relationship on your model, use the withDefault method:
class User extends Model
{
public function city()
{
return $this->hasOne(City::class)->withDefault();
}
}
With this in place, $user->city will always return a City model.

laravel model relation not working

I have created a laravel api for my application.I have used Pingpong module package for different modules.I am having hard time establishing many-to-many relation.I have 3 tables:roles,groups,group_roles.And my models are:
Group.php
namespace Modules\User\Entities;
use Illuminate\Database\Eloquent\Model;
class Group extends Model {
protected $fillable = [];
protected $table='groups';
public static function roles(){
return $this->belongsToMany('Modules\User\Entities\Role','group_roles','group_id','role_id');
}
}
Role.php
namespace Modules\User\Entities;
use Illuminate\Database\Eloquent\Model;
class Role extends Model {
protected $fillable = [];
protected $table='roles';
public function groups(){
return $this->belongsToMany('Modules\User\Entities\Group','group_roles','group_id','role_id');
}
}
And my controller
namespace Modules\User\Http\Controllers;
use Pingpong\Modules\Routing\Controller;
use Modules\User\Entities\Group;
use Modules\User\Entities\Role;
use Illuminate\Http\Request;
use App\Login;
use Input;
use Validator;
use Hash;
use Response;
class UserController extends Controller {
public function getGroupById(Request $request){
$groups=Group::with('roles')->get();
return Response::json ([
'status'=>'ok',
'group'=>$groups
],200);
}
}
The problem is I am not able to establish the relation between the models and the getGroupById returns 500 internal error response.$group=Group::all(); $group=Group::find($request['id']); returns fine but it is not returning related roles.
Similar structure and codes work fine on app without the use pingpong.
Your relationships are currently like this:
// not sure why this is static?
public static function roles(){
return $this->belongsToMany('Modules\User\Entities\Role', 'group_roles', 'group_id', 'role_id');
}
public function groups(){
return $this->belongsToMany('Modules\User\Entities\Group', 'group_roles', 'group_id', 'role_id');
}
Please note from the docs, regarding the belongsToMany method:
The third argument is the foreign key name of the model on which you are defining the relationship, while the fourth argument is the foreign key name of the model that you are joining to...
So with this in mind I think your relationships may be incorrect due to using the wrong arguments on your belongsToMany method calls. I think it should be like this:
public function roles(){
return $this->belongsToMany('Modules\User\Entities\Role', 'group_roles', 'group_id', 'role_id');
}
public function groups(){
return $this->belongsToMany('Modules\User\Entities\Group', 'group_roles', 'role_id', 'group_id');
}
Also if you have intermediate table columns you'd need to declare those on the belongsToMany call.
Hope that helps!
Edit
Firstly, you said getGroupById returns 500 internal error response. Have you tried checking what the actual error is!? 500 internal error doesn't provide much info, I'm sure you'd get to the bottom of things a lot faster if you found out the exact issue through laravel's usual error response page.
I assume you're doing this through an ajax request so you could use the network tab if you're using chrome then click on the 500 request to see the error laravel returns or you can use something like postman and hit the url through that.
If I wanted to quickly check the functionality of the models relationship methods, I'd do the following:
After setting up some data for a group and relationship, could you try running this in tinker or a route for testing/debugging.
$g = Group::first(); // get the first group, or you could use find($id) if you had a specific group in mind
// if you're in tinker
$g->roles; // show the roles
// if you're running on a route
dd($g->roles); // show the roles
While haakym's answer is very detailed, but you can also try changing your mapping table name to convention based 'group_role' instead of 'group_roles'. With this method you will have to supply only one argument to belongsToMany call.
Note that in general it should not matter if the other arguments are correct, but its just another step to debug!

1:1 relationships in Laravel returns Undefined property

I keep getting this error in Laravel 5 when working on a 1:1 database relationship:
Undefined property: Illuminate\Database\Eloquent\Collection::$owner
In my controller i have method "stuff". When i return $stuff i get:
[{"id":4,"demoId":2,"slug":"loremipsum","languageId":1,"countryId":1,"created_at":"-0001-11-30 00:00:00","updated_at":"-0001-11-30 00:00:00"}]
The relationship is on "demoId".
In my model i have this:
public function owner(){
return $this->belongsTo('App\Demotable2');
}
I'm using this code, that gives an error:
$routine = $stuff->owner->get()->toArray();
I expect to get the info in the demotable2. What do i do wrong?
There are certain rules that you need to apply when you try to make most of eloquent (convention over configuration), the problem you have is in naming your foreign key's, when you use :
public function owner(){
return $this->belongsTo('App\Demotable2');
}
eloquent expects to find a foreign key demo_id instead of demoId, when you change the name of the foreign key, you need to specify it in the relationship like so :
public function owner(){
return $this->belongsTo('App\Demotable2', 'demoId', 'id');
}
you read more here : http://laravel.com/docs/5.1/eloquent-relationships
I think this code will be in your model.
public function owner(){
return $this->belongsTo('App\Demotable2');
}
The relationship is defined in your model, not controller.
In App\Stuff.php
public function owner() {
return $this->belongsTo(App\Demotable2::class);
}
When this relationship is run, it will automatically look for a owner_id on your stuff table. However, you are using demoId. For that to work you have to define the foreign key in your relationship
public function owner() {
return $this->belongsTo(App\Demotable2::class, 'demoId');
}

Resources