Populating $attributes with values from the previous model? - laravel

I've got a Laravel project (actually a Laravel Nova project) that involves entering a lot of data. To save some time I'd like to pre-fill some of the fields in my form, based on the logged in user's last entry.
I can pre-fill fields via the $attributes variable on my model, called Product, like so:
protected $attributes = [
'category' => 'ABC'
];
And I can do this for more dynamic data in the constructor like so:
function __construct() {
$this->attributes['category'] = Str::random();
parent::__construct();
}
But I'm not quite sure how I'd go about this when I want to retrieve what the user entered last time. For example, I'd like to do this:
function __construct() {
$user = auth()->user()->id;
$last = Product::where('created_by', $user)->latest()->first();
$this->attributes['category'] = $last['category'] ?? null;
}
However that ends up in an infinite loop. Same if I call $this->where('created_by' ...
Is there a way I can set $attributes of a new Product based on the last Product created by the user?

Nova fields have resolveUsing method, so in your case if you want to populate Text field:
Text::make('Category')->resolveUsing(function () {
return optional(auth()->user()->products()->latest()->first())->category;
})

I found the solution in the Nova Defaultable package.
Once you add the necessary traits, you can just add ->defaultLast() to a resource field and it'll default to the last set value. This also works for relationships which is perfect for my use case.

Related

how to show authenticated user data from relations

I'm trying to show the data of the authenticated user from his relation with other tables but can't get it to work, and I'm pretty new to laravel.
the user table has a relation with level table thru level_id, and the level table has a morph relation with the languages table, I'm trying to show the language of the level of the current user
here is my user model relation
public function level()
{
return $this->belongsTo(Level::class, 'level_id');
}
and my level model
public function languages()
{
return $this->morphMany(Language::class, 'langable');
}
and in the language table, I need to get back the title of 0 or 1 like
languages['0']title.
here is my controller
public function profile()
{
$user= User::with('level')->with('offers')->get();
return view('pages.user.index',compact('user'));
}
and here is how I got the auth user
{!! auth()->user()->first_name . ' ' . auth()->user()->last_name !!}
I'm trying to get this to work
{{auth()->user()->level()->languages()->title['0']}}
but it shows me this
Try {{ $user->level->languages->title['0'] }} in your index.blade file since you are passing the $user var from your controller to it. Currently you are using the user from session.
i got the answer guys it goes like this
{{Auth::user()->level->languages[0]->title}}
that'd show the level of the current user
Okay, there are a few steps you need to get done
in your controller:
public function profile()
{
$user = Auth::user(); // gets the logged in user
return view('pages.user.profile', compact('user')); // return view with $user variable
}
in your user model add:
// appends the level data to the $user model
// so every time you retrieve a user, the level data is included and accessible
// after that you can use $user->level in your view file
protected $appends = [
'level'
];
in your level model add:
// appends the languages data to the $user model
// so every time you retrieve a level, the languages data is included and accessible
// after that you can use $level->languages in your view file
protected $appends = [
'languages'
];
NOTE: In Step 3 you are retrieving multiple languages for a level. Is that correct ?
usage in view
$user->level->languages[specificLanguage]->title // if level has multiple languages
$user->level->language->title // if level has one language

How can fill the user id field with auth user on creation of resource?

I'm sure there must be a very simple way to do this - I would like to fill the user_id field on my resource with the authenticated user's id whenever a new instance of the resource is created.
In the store() method of my Resource model I have:
public function store(Request $request)
{
$input = $request->all();
$resource = Resource::create($input);
$resource->user_id = Auth::user()->id;
return $resource;
return 'Resource added.';
}
This works through a post API route, however whenever I add a new resource instance through Nova dashboard, it does not add the Auth user id. I'm guessing this because Nova doesn't use that Resource controller that I have set out?
I would appreciate suggestions!
Working on the assumption that you have relationship method User::resources() the following should work:
return $request->user()->resources()->create($request->all());
The way you have it doesn't work because you didn't save the resource after associating user with it.
I haven't used Nova yet, but since its also laravel and most likely Eloquent ORM I can tell the following.
In these two lines you've set the user_id but you haven't persisted the change:
$resource->user_id = Auth::user()->id;
return $resource;
You should add this line to save the changes you've done:
$resource->save();
As an alternative you could add the value already into your $input array:
$input = $request->all();
$input["user_id"] = Auth::user()->id;
$resource = Resource::create($input);
I just created an ResourceObserver. Saved the Auth()->user->id with the created method.
Registered the ResourceObserver to AppServiceProvider and NovaServiceProvider.
https://laravel.com/docs/5.7/eloquent#observers
https://nova.laravel.com/docs/1.0/resources/#resource-events
In the model class, add function like this:
public function save(array $options = array())
{
$this->user_id = auth()->id();
parent::save($options);
}
Just be carefull if you tend to use save in any other scenario so you don't overwrite existing user_id.

How to set form data for a Joomla subform?

I'm trying to create a Joomla (3.x) component and struggling with using subforms. There doesn't seem to be much documentation for using subforms besides e.g. https://docs.joomla.org/Subform_form_field_type
For my component I have one parent table and some associated database rows from a child table.
The idea is to display an edit form for that parent table using Joomla's XML syntax for forms and in that edit form also display a subform with multiple items (the associated rows from the child table).
I would like to be able to modify the parent table fields but also in one go the associated child table rows (of course one could just edit each row associated to the parent table individually but I'm guessing that would be a terrible user experience). Or am I approaching this thing the wrong way?
Now, I know how to implement/show a subform and also know how to show the parent table fields and populate those fields with the right data. But how do I populate or refer to the subform using the parent form?
I have this function inside my component model (which inherits from JModelAdmin).
protected function loadFormData()
{
$data = JFactory::getApplication()->getUserState('com_mycomp.edit.parent.data', array());
if (empty($data))
{
$data = $this->getItem();
// how to refer to subform fields inside $data?
}
return $data;
}
I know if a field is called name or title I can just change the $data object after $this->getItem(), e.g. $this->set('name', 'John Doe').
Let's say the field of type subform has a name attribute of books and I wanted to insert one or more rows, how would I refer to it? I've tried dot syntax in various forms, e.g.: $data->set('books.1.childfield') or $data->set('books.pages1.childfield'). But it doesn't seem to refer to the right form.
There is of course getForm function in the same model file, however I do not think a subform should be loaded independently of the containing parent form?
public function getForm($data = array(), $loadData = true)
{
$app = JFactory::getApplication();
$form = $this->loadForm('com_mycomp.parent', 'parent', array('control' => 'jform', 'load_data' => $loadData));
if (empty($form))
{
return false;
}
return $form;
}
EDIT:
Already answered my own question.
Never mind. I figured it out after taking a break for some time and trying again (inspecting the form inputs again and taking a deep breath).
This is the format used:
$data->set('nameofsubformfield',
[
'nameofsubformfield0' => [
'fieldwithinsubform' => 'value-of-field-within-subform'
]
]);
This seems to work! I'm using this within getItem function now. Just have to loop and put loop counter in place of the zero after nameofsubformfield. See code below for some context (function resides in parent model).
public function getItem($pk = null)
{
$data = parent::getItem((int)$pk);
if (empty($data))
{
return false;
}
$childModel = JModelLegacy::getInstance('child', 'MycompModel');
$rowChildren = $childModel->getChildrenByParentID((int)$data->get('id'));
$childArray = [];
for ($i = 0; $i < count($rowChildren); $i++)
{
$childArray['children'. $i] = [
'name' => $rowChildren[$i]['name']
];
}
$data->set('children', $childArray);
return $data;
}

Fluently updating an existing one to one relationship with Eloquent in Laravel 5

I have a one-to-one relationship between my User model and an additional UserInformation model in which I store additional needed information which would bloat the "normal" user table.
I set up my models like this:
# User.php
public function information()
{
return $this->hasOne(UserInformation::class);
}
# UserInformation.php
public function user()
{
$this->belongsTo(User::class);
}
I have a profile page where the User can update information from both tables.
The view has inputs like this:
<input name="email"> // is a field in the users-table
<input name="information[size]"> // is a field in the users-information table
I read in different locations that I should be able to save both my User model and its relation in with:
$user->fill($request->all())->save();
But this throws the following error:
preg_replace(): Parameter mismatch, pattern is a string while replacement is an array
So my current solution looks like this:
auth()->user()
->fill($request->except('information'))
->save();
auth()->user()
->information
->fill($request->input('information'))
->save();
That works very good but doesn't look good in my opinion. So my question is: how can I clean that code up and save both in one go?
Have you tried including this
protected $guarded = array('information');
in your User.php model file
and then
auth()->user()
->fill($request->all())
->information->fill($request->input('information'))
->save();
I think your current solution looks fine, but if you want, you could always extract it out to your own custom method in your User model.
public function saveWithInformation($attributes)
{
$this->fill($attributes)->save();
$this->information->fill($attributes['information'])->save();
}
Then you can just call:
auth()->user()->saveWithInformation($request->all());
Since I was looking for a pretty flexible solution I came up with this function which I implemented into my User model (but it could also be included in a BaseModel)
public function fillWithRelation(array $request)
{
foreach ($request as $key => $value)
{
if (is_array($value) && method_exists($this, $key))
// check if the value is an array and if a method with the name of
// the key exists (which would be the relationship
{
$this->{$key}->fill($value);
unset($request[$key]);
}
}
return $this->fill($request);
}
This is definitely working if you include the information for a hasOne relationship like posted in my question.

Return Eloquent model with different name from database tables

I want to return a JSON of an Eloquent model, but I'd like to change the array keys. By default they are set as the table field names, but I want to change them.
For example if I have a users table with two fields : id and user_name
When I return User::all(); I'll have a JSON with "[{"id" => 1, "user_name" => "bob}] etc.
I'd like to be able to change user_name to username. I haven't found the way to do it without an ugly foreach loop on the model.
I'm not sure why you would want to do this in the first place and would warn you first about the structure if your app/would it be better to make things uniform throughout.. but if you really want to do it.. you could do:
$user = User::find($id);
return Response::json(array('id' => $user->id, 'username' => $user->user_name));
That will return a JSON object with what you want.
You can also change the name of the key with:
$arr[$newkey] = $arr[$oldkey];
unset($arr[$oldkey]);
Just have a look at robclancy's presenter package, this ServiceProvider handles those things you want to achieve.
GITHUB LINK
Just set the $hidden static for you model to the keys you want to hide:
class User extends Eloquent
{
public static $hidden = 'id';
}
and name them the way you like with get and set functons.

Resources