Laravel Nova Action - Attach Multiple Pivot Records - laravel-nova

I'm trying to add multiple pivot records ('activity_student') based on my 'Activity' model. An activity belongs to a 'Group' and groups belongs to many 'Students'.
I'm trying to add pivot records on my 'activity_student' table from my activity resource/model.
Below is the code of my Laravel Nova Action 'AddParticipants' triggered from 'Activity' resource
public function handle(ActionFields $fields, Collection $models)
{
foreach ($models as $model) {
// get students from the group where the activity belongs to
$students = $model->group()->students();
// attach activity and students to pivot table
foreach ($students as $student) {
$model->students()->attach($student->id);
}
}
}
Error message = Call to undefined method
Illuminate\Database\Eloquent\Relations\BelongsTo::students()
Not sure which part of the code is wrong. Or perhaps there an easier Laravel/Nova way to do it?

Related

Is it possible to reference another table so that an employee record is associated with a user record?

I am attempting to build a web app that allows users to view company policies, procedures, newsletters, and their own employee information.
I have my staff table that contains all the employee information (along with related lookup tables for things like prefix, staff grade, etc)
my app uses asp-identity for the login functionality, but I want to be able to return information from the staff table that is only relevant to that particular user.
I know it is possible to extend the ASP.net users table to include custom fields, but this doesn't really suit my goal as the staff table is used in a desktop based app by the admin team.
Add a field to your Staff table UserId for example ALTER TABLE Staff ADD UserId NVARCHAR(256) DEFAULT NULL;
Optionally, you would reference the AspnetUsers table.
Update Staff table rows in other to set UserId values to related users ids (manually or create an action to do that)
Then, In your the controller, you can select newsletter from table where employee's user id equal connected User.Identity.Id. for example
var news = context.Newsletters.Where(n=>n.Staff.UserId==User.Identity.Id);
var infos = context.StaffInfos.Where(si=>si.Staff.UserId==User.Identity.Id);
in case the tables are not in relationship, you'll need to do like following
var employee = context.Staffs.FirstOrDefault(s => s.UserId == User.Identity.Id);
if(employee != null) {
var infos = context.StaffInfos.Where(si=>si.IdEmployee==employee.EmployeeId);
return View(infos);
} else {
return Content("You don't have an account associated to your staff info...");
}
Please replace fields in these queries with the names of your fields.

Yii2: How to join tables with Query Builder?

I'm using the Yii2 framework for a very basic stock software. I have two tables of a very basic stock software:
products = (id_product, name)
movements = (id_movement, quantity, date)
The products table has all the product of a supermarket.
When the supermarket buy a product, it is registered in the movements table.
I want to show a grid with the current stock in the index.php view. I am thinking to have a actionIndex function to get the total of each products.
Basically I want to do this:
public function actionIndex()
{
Get movements and products from database;
For each product {
if (movement is IN) {
add();
}
else {
subtract();
}
}
return...
}
Using Gii Code Generator, I made a CRUD for products and a CRUD for movements.
So first, I need to get the products and movements from database. Without Yii, I can make a simple JOIN query. But I have no idea how to do it using Yii. What is the best way to do it? I would like to use the query builder.
I found this question but I didn't work for me. And this documentation but I really don't understand it.
You can use innerJoin or similar function
$result= (new Query())
->select('table1.col1, table1.col2,,,, table2.co1, table2.col2 ...')
->from('products ')
->innerJoin('movements', 'product.key= movements.key');
http://www.yiiframework.com/doc-2.0/guide-db-query-builder.html

MVC EF get many-to-many relationship table

I set up my many-to-many relationship in EF. But I can only refer to the main tables, not the bridge table. How can I refer to the bridge table?
for example:
User: UserID, name
Group: GroupID, group
UserGroup: UserID, GroupID
I can refer to the User table and Group table by typing
context.User.(some linq expression)
context.Group.(some lin1 expression)
But I cannot refer to the bridge table by typing
context.UserGroup
It would be great to know which versions of MVC and EF you are working with. Based on the assumption that its MVC 4 and EF5...
The point of a Join table is to keep referential integrity going. As #timothy says, there is no reason to refer to the (UserGroup) Join table unless there is actual additional data in it i.e. payload. If you're putting other data in the UserGroup table then its not a Join table anyway.
Rather use some Lazy or Eager loading to get the Group data you need:
Lazy Loading
var users = context.Users
foreach (User x in users)
{
foreach (Group f in x.Groups)
//notice the x user being referred to here for Groups
{
// Whatever you want to do with the objects
}
}
Eager Loading
var users = context.Users.Include(g => g.Groups)
foreach (User x in users)
{
foreach (Group f in x.Groups)
//notice the x user being referred to here for Groups
{
// Whatever you want to do with the objects
}
}
The 'foreach' code above is just added to show how to include it in a loop example. Its the first line of these Loading examples that are important.
Once you have defined 'var users' the rest is "easy".
Good Luck!
When defining a many-to-many relationship you have two options.
Link User to Group, and let it know that UserGroup is your join table
Link User to UserGroup as many-to-one, also link Group to UserGroup using many-to-one.
The reason to use option 2 is when you have something other than just the two IDs in the join table. An example I had recently was a SortOrder field.
When querying with a materialized join table, it feels a little clumsy:
context.User.Select(user => new {
Name = user.Name,
Groups = user.GroupJoins
.OrderBy(groupJoin => groupJoin.SortOrder)
.Select(groupJoin => groupJoin.Group.Name)
});
Why you want refer to that junction table...?!
If you want to get Users of a group:
var users = from u in db.UserGroups.Include("User").Include("Group")
Where u.GroupID == gid
select u;
And simply vice versa if you need Groups of a User:
var groups = from g in db.UserGroups.Include("User").Include("Group")
Where g.UserID == uid
select g;

Generating Checklist from Joined Tables in MVC3

I am part of a team working on an MVC3 project. None of us are (yet) MVC3 experts, so I'm not even sure how to ask the question properly. I'll have to just dive in and hope it makes sense to you MVC-experienced folk.
We have an HTML data entry screen with 3 component sections: a large scrolling list on the left, a detail box in the center, and another scrolling list on the right. The left-side list is a list of group codes and names drawn from a Groups table. This is drawn by a partial view called _Groups. The center area displays the detail fields for the currently selected group. The right-side list is a scrolling list of person names. The center and right are both drawn in a partial view called _GroupDetails.
The right-side list is what's giving us problems right now. We have the Groups table, the Persons table, and an intersect table which stores a Group ID and a Person ID. If a person belongs to a group, then there's an intersect record for that person-group pair. If not, then there isn't. We want to take the currently selected Group ID and use it for a query that draws all records from the Persons table and adds a boolean field. The boolean field should be T if the person belongs to the currently-selected group, and F otherwise. Then we want to display this as a checklist with the boolean field as the checkbox field and the name next to it.
What is the best way to do this?
-- Jon
Additional information, which I hope will be useful: Our data model for this view includes this List definition:
public List<GlobalName> UserList { get; set; }
Is it possible to write a constructor for this list that includes a query, or a series of queries, that end up with the list containing the elements I want: a checkbox, the user name, and the user ID (primary key) with the checkbox set as described above?
MVC doesn't come with a CheckBoxList helper method built in as it does for SelectList. However, there are plenty of great extensions around so you don't need to reinvent the wheel. You can check here, here, or here for some examples.
Using the MvcCheckBoxList extension, for example, you can do this:
// You'll need a view model
class GroupPersonsCheckListViewModel
{
public IEnumerable<Person> Persons{ get; set; }
public Group Group { get; set; }
}
// In your controller action:
var group = context.Group.Find(id); // assumes 'id' was passed to the action
var allPersons = context.Persons.OrderBy(person => person.Name);
var groupPersons = new GroupPersonsCheckListViewModel() { Persons = allPersons, Group = group };
return PartialView(groupPersons);
// In your partial
#Html.CheckBoxListFor("GroupPersons",
m => m.Persons, // Collection of all persons
p => p.PersonId, // Person ID attribute
p => p.Name, // Person Name attribute
m => m.Group.Persons // Collection of persons that are currently associated with the group
);

Entity framework many to many relation bottleneck in inserting data

i have a view that shows the user a form and the user should upload a file and choose all the categories associated with it.
the controller that is responsible in submitting the data should
retrieve the file info and
insert data in the file category
retrieve the related category ids and
insert them as well in the table
that is abstracted by the EF just
insert the file and the category ids.
this is my problem the controller just gets some info about the category not all of it. basically it only needs the ids for the insertion
i can't use
[HttpPost]
public ActionResult SaveFile(File file, List<Category> Checkbox, HttpPostedFileBase FileUpload)
{
//some stuff
//for example got the first category and named it to category1
file.Categories.Add(category1)
}
i asked someone and he told me you have to select the category you want to insert
is this really necessary ? i only need a category id and a file id to make the insert why would i fire another request to the database that i don't really need
i am using
EF 4
MVC 3
It is better to select category first because it will save you a lot of possible problems but it is not necessary. You can use dummy category object:
var category = new Category { Id = receivedId };
file.Categories.Add(category);
You will only create new category and you will set its PK. Now you need to handle file insertion where you must explicitly instruct ObjectContext to insert only file (because your categories exists in database):
context.Files.Attach(file); // now whole object graph is attached but marked as Unchanged
context.ObjectStateManager.ChangeObjectState(file, EntityState.Added); // mark only file entity as inserted
context.SaveChanges();
You can also take opposite direction:
context.Files.AddObject(file); // all objects in object graph are marked for insertion
foreach (var category in file.Categories)
{
// you don't want to insert categories again
context.ObjectStateManager.ChangeObjectState(category, EntityState.Unchanged);
}
context.SaveChanges();
This scenario works if you know that all categories exist in your database. If you want to insert new categories together with saving file you will need to query categories first or add some information about which category is new and which is existing.

Resources