Couldn't find this in the docs.
Is there any standard way, without creating a custom widget, or overriding the view template, to show a Many to Many relationships in a CRUD's showOperation in Backpack for Laravel? If the answer is NO, what would be your approach to implement it?
Let's say I have a Course Model, and a User model, and there is a Many to Many between both
class Course extends Model
{
public function students()
{
return $this->belongsToMany(User::class, 'course_students');
}
}
class User extends Model
{
public function courses()
{
return $this->belongsToMany(Course::class, 'course_students');
}
}
In the Show Operation for the Course. How do I show a Table with all students?
Indeed, you can use the relationship column for this
Excerpt:
Output the related entries, no matter the relationship:
1-n relationships - outputs the name of its one connected entity;
n-n relationships - enumerates the names of all its connected entities;
Its name and definition is the same as for the relationship field
type:
[
// any type of relationship
'name' => 'tags', // name of relationship method in the model
'type' => 'relationship',
'label' => 'Tags', // Table column heading
// OPTIONAL
// 'entity' => 'tags', // the method that defines the relationship in your Model
// 'attribute' => 'name', // foreign key attribute that is shown to user
// 'model' => App\Models\Category::class, // foreign key model
],
Backpack tries to guess which attribute to show for the related item.
Something that the end-user will recognize as unique. If it's
something common like "name" or "title" it will guess it. If not, you
can manually specify the attribute inside the column definition, or
you can add public $identifiableAttribute = 'column_name'; to your
model, and Backpack will use that column as the one the user finds
identifiable. It will use it here, and it will use it everywhere you
haven't explicitly asked for a different attribute.
Related
I'm trying to get first name and last name from a related table
I've tried using an accessor on the related table which works fine when loading the page and for adding/editing but when searching it shows an error
Column not found: 1054 Unknown column 'FullName' in 'where clause'
This column obviously does not exist.
So I have this in my related model
{
return $this->attributes['first_name'] . ' ' . $this->attributes['last_name'];
}
And under the Backpack crud Controller I have
$this->crud->addColumn([ // n-n relationship (with pivot table)
'label' => 'Account', // Table column heading
'type' => 'select',
'name' => 'user_id', // the method that defines the relationship in your Model
'entity' => 'user', // the method that defines the relationship in your Model
'attribute' => 'FullName', // foreign key attribute that is shown to user
'model' => "App\Models\BackpackUser", // foreign key model
]);
Where am I making a mistake, input would be very much appreciated.
That needs a real column.
You can use a view (like you already do) or a model_function column to format the table cell value in your model class - https://backpackforlaravel.com/docs/3.5/crud-columns#model_function
Just see what's simpler for you.
Note: if ordering or searching doesn't work as expected see https://backpackforlaravel.com/docs/3.5/crud-columns#custom-search-logic-for-columns and https://backpackforlaravel.com/docs/3.5/crud-columns#custom-order-logic-for-columns
I am following this exercise of an "intermediate task list" from Laravel 5.2 documentations and have some difficulty understanding how this piece of code works.
$request->user()->tasks()->create([
'name' => $request->name,
]);
Question 1
Specifically, I am confused at the relation between the user() and tasks() methods. Why and how exactly can we make the user() and tasks() methods available from the $request object?
Question 2
I created a similar app and has Person and Country models. I want to pass the country_id input from a dropdown list, but I can't get the database updated, using the following code.
public function store(Request $request)
{
$this->validate($request, [
'name' => 'required|max:255',
'country_id' => 'required|max:3',
]);
$request->user()->people()->create([
'name' => $request->name,
'country_id' => $request->country_id,
]);
return redirect('/people');
}
Why is it that the country_id cannot be saved to the table? I tried changing the user() to country() but the error message says, "class Country" was not found. But why is the 'user()' in the tutorial available then? I am baffled.
The user() method of request is available if the user making the request is authenticated.
Since it uses User model it can also fetch the related model. For tasks() to work Task relationship must be defined in user model.
For country_id to be created(mass assigned) ensure its included in $fillable property of the People model
$fillable = ['country_id']
Please read Eloquent model relationship section in Laravel documentation.
I have 2 entities,
News
FileAttachment
I wanted to configure using code-first fluent API so that Each News can have 0,1 or more than 1 attachments.
here is what i'm using right now
public NewsMap()
{
this.ToTable("News"); // Table Name
this.HasKey(m => m.Id); // Primary Key
// Field Definition
this.Property(m => m.Title).HasMaxLength(255).IsRequired();
this.Property(m => m.Body).HasColumnType("Text").IsRequired();
this.Property(m => m.Summary).HasMaxLength(1000).IsRequired();
this.Property(m => m.AuthorId).IsRequired();
this.Property(m => m.CreatedOn).IsRequired();
this.Property(m => m.UpdatedOn).IsRequired();
this.HasMany(m => m.Attachments).WithMany().Map(m => m.MapLeftKey("NewsId").MapRightKey("AttachmentId"));
}
public class FileAttachmentMap : EntityTypeConfiguration<FileAttachment>
{
public FileAttachmentMap()
{
this.ToTable("FileAttachments"); // Table Name
this.HasKey(m => m.Id); // Primary Key
// Field Definition
this.Property(m => m.DisplayName).HasMaxLength(256).IsRequired();
this.Property(m => m.PhysicalFileName).HasMaxLength(256).IsRequired();
this.Property(m => m.Extension).HasMaxLength(50).IsRequired();
this.Property(m => m.IsImage).IsRequired();
this.Property(m => m.ThumbTiny).HasMaxLength(275).IsOptional();
this.Property(m => m.ThumbSmall).HasMaxLength(275).IsOptional();
this.Property(m => m.ThumbMid).HasMaxLength(275).IsOptional();
this.Property(m => m.ByteSize).IsRequired();
this.Property(m => m.StorageType).IsRequired();
this.Property(m => m.CreatedOn).IsRequired();
this.Property(m => m.UpdatedOn).IsRequired();
}
}
This mapping correctly generates an intermediate table named NewsFileAttachment with two fields :
NewsId
AttachmentId
On News Entity when i call News.Attachments.Add(Attachment); it correctly adds records in both Attachment & NewsAttachment tables.
When i remove some list item from News.Attachments it correctly removes record from NewsAttachment table, but it doesn't delete record in FileAttachment table. I wanted to remove that too.
Can someone please suggest a better Fluent API configuration to achieve this?
Thanks,
Amit
EDIT
In my case FileAttachment stores files for various purpose. i've Blog entity that too have attachments. So, two intermediate tables BlogAttachments & FileAttachments. Now if i use WithOptional as (I can't use WithRequired as i need BlogId & NewsId both in FileAttachment table), i can get rid off intermediate table, but still delete doesn't delete record from FileAttachment table, it just make NewsId/BlogId NULL.
Any suggestion? Main thing is I do not wanted to create separate tables with all the fields i have in FileAttachment table.
That's expected - as it creates many-to-many and extra table - the cascade only applies to that table.
There is no direct 'FK' relationship in between your News and
Attachment, as it goes through a join table. And thus you cannot expect for e.g. attachment to be deleted, if the news does - as attachment could have other news relating to it.
See also this one - it's somewhat relevant.
One to Many Relationship with Join Table using EF Code First
i.e. if your structure permits don't explicitly create many-to-many (don't put collection on both sides, or similar in fluent config).
In your case providing your 'attachments' are not reusable in between News - then just put a collection navigation property in the News - and leave attachment w/o any - or make a 'FK', single instance navigation from Attachment (like a 'Parent') if you need it.
On the other side, if an attach... could be parented by different
news records - then you shouldn't have cascade delete anyways.
note: check your generated migration script - or SQL/Db - to see exactly what it creates - and make sure there is no intermediate table created - and only one 'FK' going from 'attachment' to 'news'.
edit:
modelBuilder.Entity<News>()
.HasMany(c => c.Attachments)
.WithOptional() // or WithRequired (test to see which is better for you)
.WillCascadeOnDelete(true);
...and make one public ICollection<FileAttachment> Attachments {get;set;} in the News.
(actually the collection property is all you need - but configuration is to be safe you get what you want)
That'd make you 1-to-many (or many-to-one), which is the nature of your data (as you said in comments) - and you can have cascade deletes.
My Product entity has the following structure:
private $id;
private $title;
/**
* #ManyToOne(targetEntity="Category")
* #JoinColumn(name="cat_id", referencedColumnName="id")
*/
private $category;
Category have nested structure. And each level of nesting is shown in 5 separate fields:
In class form code, I solve it in this way:
$builder
->add('cat_1', 'entity', array(
...
'query_builder' => function() { return someSelectLogic1(); }
))
->add('cat_2', 'entity', array(
...
'query_builder' => function() { return someSelectLogic2(); }
))
->add('cat_3', 'entity', array(
...
'query_builder' => function() { return someSelectLogic3(); }
))
->add('cat_4', 'entity', array(
...
'query_builder' => function() { return someSelectLogic4(); }
))
->add('cat_5', 'entity', array(
...
'query_builder' => function() { return someSelectLogic5(); }
))
Now I need to know which field is filled in the last turn and pass the value of that field in the entity property.
In all that I do not like:
complex logic to determine which field with category was filled at the end
each of these fields is not tied to the entity 'mapped' => false
1) What the right way to organize code of my form?
2) And is there a way to bring these fields into a separate class which will deal with the logic of determining which category was chosen in the end?
I would suggest the following:
1) Create a new custom form field type and put all those entity in there.
This process is not much different from ordinary creation of form type. Just enclose those fields in it's own buildForm() and that should do the trick. Docs.
2) Mark all those entity fields with property "property_path => false".
Clearly you wont be storing these values inside your model.
3) Add two more fields: chosen and lastOne.
Now, this might be tricky: I would either set the chosen to text type (basically, generic type) or would use entity as well. If you go for entity you would need to include all possible answers from all entity fields. As for the lastOne set it to text as it will reflect which field (by name) was selected last.
Either way, those two fields will be invisible. Don't forget to set property_path to false for lastOne field.
4) Finally, add ValueTransformer (docs) which will contain logic to "see" which field was selected last.
Now, I dealt with it only once and don't understand it just quite yet, so your best bet would be trial and error with examples from official docs, unfortunately.
What basically you should do is to, within value-transformer, read the value of field lastOne. This will give you the name of field which was selected last. Then, using that value, read the actual last value selected. Last, set that value (object, if you've went for entity type, or it's ID otherwise) to chosen field.
That should basically do the thing.
As for the JS, I don't know if you're using any framework but I will assume jQuery. You will need to set lastOne field as your selecting items in your form.
$(function(){
$('#myform').find('select').on('change', function(){
var $this = $(this);
$this.closest('form').find('#__ID_OF_YOUR_LASTONE_FIELD').val($this.attr('name'));
});
});
I'm sorry I cannot provide you with code samples for PHP right now. It's a bit late here and will do my best to further update this answer tomorrow.
I have a handful of DBIx::Class::Core objects that model various database tables.
For some of those models (those that have a 'queue' column), I have another class inject subs (basically, to 'move' the model object along it's queue states).
I'd like to also have that class inject has_many relationships ala
class($name)->has_many('queue_history','MySchema::Result::QueueHistory',
{ 'foreign.record_id'=>'self.id' },
{ where => { type => $name }} );
but I can't seem to get the relationships to register properly (keep getting "No Such Relationship" errors - however, when calling the relationship method on the sources provides back the relationship).
Any clues as to what's wrong?
After some digging around, the following works:
$class = $schema->class($name)->has_many('queue_history','MySchema::Result::QueueHistory',
{ 'foreign.record_id'=>'self.id' },
{ where => { type => $name }} );
$schema->unregister_source($name);
$schema->register_class($name,$class);
The key being the unregister/register methods in order to generate all the appropriate other methods that get added by having a new has_many relationship.