I already have done a long investigation about this issue on the Google and GitHub issues on the Laravel Nova.
I do have next code, which is described below:
/**
* #var array
*/
private $parentClasses = [
'activity' => Activity::class,
'movie' => PlayTogether::class,
'book' => Book::class
];
/**
* Returns a parent class/entity to which Skills are described
*
* #return \Illuminate\Database\Eloquent\Relations\BelongsTo|Activity|PlayTogether|Book
*/
public function parent()
{
return $this->belongsTo($this->parentClasses[$this->attributes['type']], 'entity_id', 'id');
}
When accessing the edit mode, on the Update I am getting an exception:
While logging $this->attributes['type'], I am getting an empty string.
How could I solve this issue? Any thoughts?
I have the similar issue with one of my models. The model have dynamic relational field, and issue occured when trying to update this model. When updating the model - all it's fields and relations are empty. I 'solved' this issue by checking field on emptiness in places where it's needed.
if (!empty($this->relatedModel)) {
// do someting
}
Seems to be a bug inside Nova.
Related
so i just wondered, if something like this is possible, since my code does not work.
protected $appends = ['position_name'];
public function getPositionNameAttribute()
{
return $this->belongsTo('App\EmployeePosition', 'employee_position_id')->name;
}
Can I append the name of Eloquen relationship model?
edit: so far, i am using this:
foreach ($employees as $e) {
$e->position_name = $e->position->name;
}
So, I needed to use the relation defined before.
protected $appends = ['position_name'];
public function position()
{
return $this->belongsTo('App\EmployeePosition', 'employee_position_id');
}
public function getPositionNameAttribute()
{
return $this->position->name;
}
Based on your comments i'd suggest to use the laravel default solution for your problems API resrouces
eg
class EmployeeResource extends JsonResource
{
public function toArray($request)
{
return [
'id' => $this->id,
'name' => $this->name,
'email' => $this->email,
'position_name' => $this->position->name,
];
}
}
note: using the with as other people suggested to preload information can increase performance by reducing the amount of queries, if you are returning a collection of employees.
Creating an accessor that looks up a value in another model and appending this field by using $appends is bad practice and will lead to n+1 queries whenever you fetch your Employee model. You should avoid doing this and just use $employee->position->name.
You should also make sure to use Employee::with('position') when you need to show the position name, so that the position model is fetched in a single query.
If the position name is something that you need in all your Employee queries, then you can set the Employee to always eager load the position by defining the following inside your Employee model:
/**
* The relationships that should always be loaded.
*
* #var array
*/
protected $with = ['position'];
I think you can just create a model with position names and reference it to the position id in the other mode by using eloquent relationships.
I am trying to force delete model with one relation. I made a custom function for that in model.
/**
* Force delete active ingredient with relationships
* #return bool|null
* #throws \Exception
*/
public function bruteDelete()
{
$this->brandPresentations()->forceDelete();
return parent::forceDelete();
}
This is how I am calling the function
Active_ingredients::withTrashed()->find($request->get('elementID'))->bruteDelete();
Which force deletes brandPresentations perfectly. But it soft deletes the parent, which is active_ingredients.
I tried to use forceDelete() as function name to overwrite the Laravel method, but it gave me the same result.
How can I force delete both models?
You can use it in boot method like this.
protected static function boot()
{
parent::boot();
self::deleting(function (User $model) {
if ($model->forceDeleting){
$model->brandPresentations()->forceDelete();
}
});
}
And then run forceDelete
Active_ingredients::withTrashed()->find($request->get('elementID'))->forceDelete();
I hope it works :)
I would suggest using cascade on delete for your migrations
$table->foreign('model_id')->references('id')->on('models')->onDelete('cascade');
By calling $item->forceDelete() then all of its related foreign keys would be deleted as well.
I'm pretty sure this does not work for soft delete.
I have created one and I thought it works:
<?php
namespace App\Traits;
use Carbon\Carbon;
trait FormatDates
{
public function setAttribute($key, $value)
{
parent::setAttribute($key, $value);
if (strtotime($value))
$this->attributes[$key] = Carbon::parse($value);
}
}
But there is a problem when calling related models. For example if you have an Article and Tag model and you want to get all tags like this:
$article->tags
it returns null because of that getter mutator.
How to fix this?
update 17.11.2017
I have found a solution to my problem. The best way to present the date in locale is to use this function:
\Carbon\Carbon::setToStringFormat("d.m.Y H:i");
simply create a service provider or a middleware and it will show all $dates in format you want. There is no need to make a getter.
Based from this: https://laravel.com/api/5.5/Illuminate/Database/Eloquent/Concerns/HasAttributes.html#method_getAttribute
The description says:
Get a plain attribute (not a relationship).
Luckily there are another two methods below it called getRelationValue and getRelationshipFromMethod, and it reads:
Get a relationship.
Get a relationship value from a method.
respectively.
And in your example, it looks like you're calling a relation.
I think you should consider it when doing your universal getter/mutator.
UPDATE:
If you inspect the code, the getAttribute also calls the getRelationValue method. But it is the last resort of the function; if the key is neither an attribute or has a mutator or is a method of the class.
Here is the stub: https://github.com/laravel/framework/blob/5.5/src/Illuminate/Database/Eloquent/Concerns/HasAttributes.php#L302
/**
* Get an attribute from the model.
*
* #param string $key
* #return mixed
*/
public function getAttribute($key)
{
if (! $key) {
return;
}
// If the attribute exists in the attribute array or has a "get" mutator we will
// get the attribute's value. Otherwise, we will proceed as if the developers
// are asking for a relationship's value. This covers both types of values.
if (array_key_exists($key, $this->attributes) ||
$this->hasGetMutator($key)) {
return $this->getAttributeValue($key);
}
// Here we will determine if the model base class itself contains this given key
// since we don't want to treat any of those methods as relationships because
// they are all intended as helper methods and none of these are relations.
if (method_exists(self::class, $key)) {
return;
}
return $this->getRelationValue($key);
}
ANOTHER UPDATE
Since you've changed your question:
You can just put the attribute name to $casts or $dates array (in your Model) so Laravel will automatically transform it into a Carbon instance when accessing it, like this:
class Article extends Model {
...
protected $dates = ['some_date_attribute`];
or with $casts
...
protected $casts = ['some_date_attributes' => 'date'];
You really can avoid this, it's already there!
on the model Class you can do:
protected $dates = ['nameOfTheDateOrTimestampTypeField','nameOfAnotherOne'];
I have a polymorphic relationship set up in Laravel 5.4. The relationship is working but the query is using the fully qualified model name.
select * from `contact_info`
where `contact_info`.`entity_id` = '25'
and `contact_info`.`entity_id` is not null
and `contact_info`.`entity_type` = 'App\Modules\User\Model\User'
limit 1
The relationship is set on the User model:
/**
* #description Method handles polymorphic contact relationship.
* #return \Illuminate\Database\Eloquent\Relations\MorphOne
*/
public function contact()
{
return $this->morphOne('App\Modules\Common\ContactInfo', 'entity');
}
and the ContactInfo model:
/**
* #description Method establishes polymorphic relationship (tenant/user).
* #return \Illuminate\Database\Eloquent\Relations\MorphTo
*/
public function entity()
{
return $this->morphTo();
}
The actual table has the values set to 'user', 'tenant' and 'referrer' instead of the model names/namespaces. I found a little bit of information about relationship maps but don't know if that'll solve my issue here.
Basically I need to find out how to tell the code that 'App\Modules\User\Model\User' should be 'user' in the database.
MorphMaps were the way to go. I added this to my AppServiceProvider boot method and it started pulling the data as expected:
use App\Modules\User;
use App\Modules\Tenant
Relation::morphMap([
'user' => User::class,
'tenant' => Tenant::class
]);
Query is now:
select * from `contact_info`
where `contact_info`.`entity_id` = '25'
and `contact_info`.`entity_id` is not null
and `contact_info`.`entity_type` = 'user' limit 1
This article helped me out tremendously.
I am new to Symfony. I want to create a blog site. So for that I have created two entities post and user. I think I have also relate both the entities properly as described over here:
http://symfony.com/doc/current/doctrine/associations.html
AppBundle/Entity/Post.php:
...
/**
* Post
*
* #ORM\Table(name="post")
* #ORM\Entity(repositoryClass="AppBundle\Repository\PostRepository")
*/
class Post
{ /**
* #ORM\ManyToOne(targetEntity="User", inversedBy="post")
* #ORM\JoinColumn(name="user_id", referencedColumnName="id")
*/
private $user;
....
}
AppBundle/Entity/User.php:
...
/**
* User
*
* #ORM\Table(name="user")
* #ORM\Entity(repositoryClass="AppBundle\Repository\UserRepository")
*/
class User extends BaseUser
{
/**
* #ORM\OneToMany(targetEntity="Post", mappedBy="user")
*/
private $post;
...
}
I'm successfully able to save the a post with its userid(user who creates the post). But I'm not able to achieve the reverse of this. i.e get all the posts created by an user. I'm trying to achieve it like this:
$em = $this->getDoctrine()->getManager();
$user = $em->getRepository('AppBundle:User')->find($this->getUser()->getId());
dump($user->getPost());exit;
But I'm not getting any posts in the variable. (FYI: There are posts in the database with the user id). Here is what I'm getting for above:
PostController.php on line 163:
PersistentCollection {#103 ▼
-snapshot: []
-owner: User {#76 ▶}
-association: array:15 [ …15]
-em: EntityManager {#374 …11}
-backRefFieldName: "user"
-typeClass: ClassMetadata {#80 …}
-isDirty: false
#collection: ArrayCollection {#102 ▼
-elements: []
}
#initialized: false
}
Note: I'm using FosUserBundle for user management. Not sure if it is making issues over here.
Any help would be appreciated.
Thanks,
Parth vora
I got the issue here. All above code is correct. I was just forgetting to call one method to get all the posts created by an user.
$em = $this->getDoctrine()->getManager();
$user = $em->getRepository('AppBundle:User')->find($this->getUser()->getId());
dump($user->getPost()->getValues());exit;
getValues() is the method to extract the data from the PersistentCollection class.
PS: Symfony's documentation needs a huge improvement. :(
Thanks,
Parth vora