I have this scenario to implement: a collection of parentEntities, each parentEntity having zero to childEntities. I need to implement a multiple-delete form (checked parentEntities will be deleted), with this rule: if a parentEntity has children, it can be deleted only after all its children have been linked to other parentEntities.
So I have the Index form for the parents, and on submit I post to the "Delete" action. For each checked parent, if it has zero children I delete it, else I redirect to children edit view. When there is no more child linked to the initial parent, I must return to the parents "Delete" action and continue from where I left (delete the current parent whose children I just re-affected, then delete the next checked parent).
My problem is: HOW do I write the return from children edit to the parents delete?
I use:
return new RedirectToRouteResult(new RouteValueDictionary(new { controller = "Parents", action = "Index", page, IDsToDelete = idCollection }));
but I get the error:
http://localhost:64209/Parents/Delete?page=0; The resource cannot be found.
Thank you for helping.
Manu
Ok, it's solved - I just needed to pass the string of checked items pending for delete.
Related
I'm struggling with an identifiableAttribute function in a parent model.
In the child controller, this works fine:
CRUD::field('supporter_id')-> // child column for foreign key to parent
type('select')-> // the type of Backpack field you want
entity('supporter')-> // function that defines parent relationship in child Model
attribute('name_last_1') // column of parent shown in Select field
;
Each Address (the child table) is identified by the Last Name of the Supporter (the parent table).
But of course just Last Name isn't good enough. So I added this to the Supporter (parent) model:
public function identifiableAttribute() // for Backpack's dropdown fields
{
return $this->name_last_1 . ', ' . $this->name_first_1 ;
}
and eliminated the attribute('name_last_1') line from the child controller
But instead of the Supporter field showing last-name-comma-first-name, the Supporter field is empty for each child row. And if you drop down the Supporter field, it appears to have the right number of elements to select from, but they each are blank. So apparently the dropdown is working fine, but it's not getting the identifiable attribute it needs from identifiableAttribute().
I also tried return 'test' ; in identifiableAttribute() to see if that would change the symptom, but it didn't ... still nothing but blanks.
There must be something I'm not understanding about identifiableAttribute() and how to implement it. Can you see where I've gone wrong?
Thanks for the questions.
If you would like to concatenate strings, please use an acessor as the identifiable attribute.
In your model:
protected $appends = ['full_name'];
public function getFullNameAttribute() {
return $this->first . ' ' . $this->last;
}
An then use in your field definition:
'attribute' => 'full_name'
Hope it helps.
Cheers
I can't believe I've never had to do this and now I'm realizing I don't actually know how to accomplish it. The basic structure is Parent hasMany Child). I want to periodically query all Parent entries that do not have a Child entry and delete them.
I would think this starts with Parent::with('child')->where...->delete(). But how do I finish this out? Do I need to do this using DB::raw instead?
If you want to help me one step further... I have a function in the Parent model for isExpired(). I really only want to delete the entries where isExpired() is TRUE.
You are looking for doesntHave.This will give all the parents which donot have child entries. And delete them.
$parents=Parent::doesntHave('child')->get();
foreach($parents as $parent)
{
$parent->delete();
}
The relation in Parent Model:
public function child()
{
return $this->hasMany('App\Child');
}
Please can you help me with the problem below:
I have 2 domain classes(Parent, Child) which I do not want to be mapped to a table, so I put mapWith=none. However, when I do parent.validate() I want the validation to be cascaded to the child. How can I enable cascade validation for domain objects which are not mapped to a table?
Many thanks in advance!
Don't know if you can by design.
You could optionally add a customValidation() method on the parent object to initiate a check which is looping over the children and invoking their validate() methods. Any errors on the children could then be added to the errors object of the parent object.
boolean cascadedValidation() {
this.validate();
children.each {
if (!it.validate()) {
it.errors.allErrors.each { err ->
// Bind somewhere on parent object
}
}
}
return this.hasErrors();
}
Problem:
I need to save an updated detached EF object to the database.
The deatched entity is created through binding the base entity, then
foreach child ...
entity.children.add(child)
I add an Array of child entities that I also get from bind in controller.
What I want, is for the old entity to be completely replaced by the new entity, this means:
entity.property => update (easy)
entity.child => delete old child if not exists in the new,
update if already exists (easy),
or add if it doesnt exist already
What I have so far:
items tempitem = new items() { id = item.id };
_context.items.Attach(tempitem);
_context.items.ApplyCurrentValues(item);
foreach (var itemchild in item.children)
{
childEntity tempchild= new childEntity () { parentid = item.id };
_context.childEntity.Attach(tempchild);
_context.childEntity.ApplyCurrentValues(itemchild );
}
This lets me update base entity fine, and also update any existing entities fine.
It doesnt let me delete old entities - tried doing an
item.children.Clear()
but nothing happens, and also when I try to add a new child entity (that was bound to the detached) - nothing happens either.
This can't be too hard, can it? I'm thinking of turning to NHibernate where things seem less bloated and more straightforward but powerful...
Basically you must load the original object graph from the database (parent and all children, using Include for example), otherwise EF cannot know which children are new and which have been deleted. You have to add or delete the children then by comparing the loaded original child collection with the current collection in your detached entity.
An example how to do that is here: https://stackoverflow.com/a/5540956/270591
I am confused with this:
I have an action ,say Parent ,and in the corresponding view file ,I have called a child action ,say Child ,both Parent and Child actions are in the same controller.
and I need the Child action and the Parent action to share some data in the ViewBag.Now ,what I should do ?Here is my question:
when I call the Child action in parent's view file ,I pass the viewbag to it like this:
#Html.Action(ViewBag).
in my child action ,I do this:
public PartialViewResult Child(Object ViewBag)
{
//using the data in ViewBag
}
Is this the right way ? Does the viewbag object passed by reference or it is a different object then the original viewbag(more memory needed)?
Or if the Child action is sharing the viewbag with its calling parent Action by default?
From Darin Dimitrov's answer ,I knew that I can't do something like this:#Html.Action(ViewBag)
But I really need to pass the child action muti-parameters,what can I do ?
Child actions follow a different controller/model/view lifecycle than parent actions. As a result they do not share ViewData/ViewBag. If you want to pass parameters to a child action from the parent you could do this:
#Html.Action("Child", new { message = ViewBag.Message })
and in the child action:
public ActionResult Child(string message)
{
...
}
There is a way, but you have to create a custom abstract class as the base class for your razor views. Then expose whatever you need to from parent to child actions.
This is how I get the root controller's ViewBag inside a class inheriting from WebViewPage
private dynamic GetPageViewBag()
{
if (Html == null || Html.ViewContext == null) //this means that the page is root or parial view
{
return ViewBag;
}
ControllerBase controller = Html.ViewContext.Controller;
while (controller.ControllerContext.IsChildAction) //traverse hierachy to get root controller
{
controller = controller.ControllerContext.ParentActionViewContext.Controller;
}
return controller.ViewBag;
}