Yii2: Proper Structuring of Actions based on User Roles - yii2

I'm quite worried with the current way I structure actions in my controllers.
I'm not sure which is the more adopted method for implementing actions that show different things for different users based on their type.
For example:
Creating a Model when User is Type 1 uses the same action but passes more parameters to the view than User Type 2.
Creating a Model when User is Type 2 uses same action but passes less parameters to the view and hence there are if statements in the view to show/hide fields based on the User Type.
Is this a proper way of doing things? If not, can you direct me to some documentation that explains a good structure?
Thanks & appreciate your help.

A simple but trival way is this
you can pass an array (eg $param) and then evaluate the type for do the right thinghs inside your action
public function actionYourAction( $param)
{
$type = $param['type'];
switch($param['type']){
case 'TYPE1' :
....
break;
}
a more clean solution could be a proper object oriented class method specialization for user object, instantiate the proper user object where you nedd and pass thsi in action call. Inside the actione simply use the object (specilized) method .

Related

How can I configure an action that creates a variable number of objects?

I am looking to create an action type that can be used to create a variable number of objects of a given object type. In other words, a user should be able to use this action to create 3 objects at once, 5 objects at once, etc.
I tried to accomplish this using the action configuration UI in OMA, but ran into the following issues:
I noticed that in the “Rules” section, it is only possible to define a static number of objects to be created. In the example shown in the screenshot below, you’d only be able to create 2 objects rather than a variable number.
Similarly, there is no way to specify a variable number of parameters in the “Form” section, which would be necessary to capture the primary keys for each object the user would like to create. I thought of specifying a string parameter that takes in multiple values as an alternative, but that wouldn’t work because there is no way to assign a single value from this parameter to an object property.
How should I go about accomplishing this?
It would be possible to create a variable number of objects using a function backed action! In particular, you could take the following steps:
Create a new function that takes in a list of primary keys as input and creates an object for each primary key in the list. The code for this function could look something like this:
#Edits(ObjectA)
#OntologyEditFunction()
public createMultipleObjects(primaryKeys: string[]): void {
// Loop through pkeys and create a new object for each pkey
primaryKeys.forEach(k => {
Objects.create().objectA(k)
});
}
You can also reference the following documentation for more guidance on how to define Ontology edit functions.
Create an action in OMA that calls the function that you defined in step 1. You will need to define a multi-value string parameter for this action, which will be passed as an input to the function.
You can refer to the following documentation (https://www.palantir.com/docs/foundry/action-types/function-actions-getting-started/) for a step by step guide on how to configure a function backed action.

Is it possible to set an Actions submission criteria that prevents submission if an object has more than a certain number of linked objects?

I currently have an action that creates a new link between an object of type A, named OA, and an object of type B, named OB.
Our workflow has a constraint such that any object of type B can at most, be linked to 4 objects of type A. As such, I would like to define a submission criterion in the action such that submission is blocked if OB is already linked to 4 objects of type A.
I couldn't find a straightforward way to do this using the Action configuration UI. How could I accomplish this?
The easiest way to accomplish this would be to turn your action into a function backed action. This would allow you to take the following steps to accomplish the desired functionality:
You can search around to all objects of type A that are linked to OB by writing something like:
// Search around to all objects of type A that are linked to OB
const linkedObjects = OB.objectTypeA.all();
// Now get the number of linked objects
const numLinkedObjects = linkedObjects.length;
Prevent the function from running by throwing a UserFacingError if there are more than 4 linked objects
if (numLinkedObjects >= 4) {
throw new UserFacingError("Objects of type B cannot be linked to more than 4
objects of type A");
}
For reference, here are some relevant pages in Foundry’s documentation:
Creating Function Backed Actions (https://www.palantir.com/docs/foundry/action-types/function-actions-getting-started/#getting-started)
Accessing link types in Functions (https://www.palantir.com/docs/foundry/functions/api-objects-links/#link-types)
Throwing UserFacingErrors from Functions (https://www.palantir.com/docs/foundry/functions/user-facing-error/)
While you can certainly do this in a Function-backed action, the tradeoff is that you won't get up front validation of the criteria, but rather the user will submit the action and then will received a toast showing the UserFacingError text. So while this technically achieves the validation, it is a sub-par user experience compared to disabling the button with a message or otherwise catching the condition upstream of the action itself in the workflow.
An alternative, iff you're using the action exclusively through Workshop, (this won't work if you want the action to "stand alone" in object explorer), you can create an object set variable that holds the result of the search around and pass that in as a hidden parameter to the Action. You can then set up the Action submission criteria as normal to check the length of that parameter and provide a message back to the user. You can also use that information in the app itself to, for example, conditionally show or hide some other workflow for the condition.
If you take this approach, make sure to add the hubble-oe:hide-action typeclass to one of the object parameters in the Action Form configuration so that the Action doesn't show up where users could use it through Object Explorer.

Parameters on Url.Action() have null properties

I have the following code:
onClick="location.href='#Url.Action("StampPdf", "EditPdf", new {pView = currentPdfView})'" />
which when clicked calls a method called StampPdf(PdfView pView) on a controller called EditPdfController. The controller method is called, with the variable pView, but instead pView has all its properties equal to null. It's as if the PdfView class has been instantiated afresh. Within my cshtml razor file, currentPdfView is instantiated and has all properties with values assigned to them, but in the controller the properties are null.
This user here had the same issue, but there is no followup on whether it was resolved or not.
Is there something I need to do to make this work?
It doesn't know how to serialize the model into a query string :)
You need to break your model into multiple properties with simple values, or use a form POST to submit them as a model.
You can also serialize the model into e.g. JSON and pass it as a string.

ASP.Net MVC4 with Razor - Best and correct way to acces controler methods in views

I have been working with ASP.NET but I am fairly new to the ASP.Net MVC 4 with Razor concept. So my question might be basic but I appreciate every help as I couldn't really find a definite answer for days now (Or I am looking for the wrong things). The question is: How do I access a Controller method within my view without using my Index Method?
The scenario:
I have a database that store some values like date, price etc ... (Variables are defined in the Model.
I have a controller thats sets a view index
I have a view that shows Indexpage with the values of my data base.
I want to sum all values of the price colum but dont want to store the result in my db.
In my understanding I access the db via a Method in my Controller class and call this method in my view.
To learn step by step I just defined a fixed value in my controller to see how to show this value in my view.
The code:
Model:
[Range(1, 100)]
[DataType(DataType.Currency)]
public decimal Price { get; set; } // Amount of an entry
Controller:
public ActionResult Index()
{
return View(db.Amounts.ToList());
}
public ActionResult ShowSumOfAllPrices()
{
//For testing db is not called yet. Query must be defined and result written to a variable within this mehod
ViewBag.Price = 2;
return View();
}
View:
#*testmethod for displaying sumprize (first just wit test result) via controler*#
<table>
<tr>
<td>hallo #ViewBag.Price</td>
</tr>
</table>
The result is not shown as long as I don't define the method content of ShowSumOfAllPrices() (in my controller class) in the index() method.
The question is: Is a result of a method in a view only visible if I define it within my Index() method in my controller or can I write an extra method like I did and call it in my view? It is working if I c&p the logic of ShowSumOfAllPrices() to Index(). But I don't think I need to define everything in my index method. If so, it would be a large and fat method. So as far as I see there are 3 possible ways but not all might be "nice" or even working:
Define everything in the Index() method.
Define other methods but call them in the Index method because it is the only way to display it within my view and not using the model because i dont want to store those data in my db
I can define other methods and directly call them in my view without having a new page but only the result of this method shown under the content of the currend Index Page.
I hope my question is understandable and this question was not asked like mine before.
Define everything in the Index() method.
Controllers are not meant to hold the logic.
Define other methods but call them in the Index method because it is the only way to display it within my view and not using the model because i dont want to store those data in my db
Don't define other methods in the controller itself. Make other classes and then call those methods in the Index() or any suitable controller method. And if you want to display data in the view , then model is the best choice, but you can even store your data in the ViewBag
I can define other methods and directly call them in my view without having a new page but only the result of this method shown under the content of the current Index Page.
If you want to fetch the result of the methods in the same view then this can be the case[might be using same kind of AJAX] but if you want to render other page , then this might be a bad idea.
The best thing you can do is to declare a class for your required function as Single responsibility principle and then use the class to do that operation in the method you want to have results in.You can access the controller methods using AJAX and JQuery, just google it out a bit and you will be your own.
Hope this helps.

How to return multiple classes as IQueryable<T>

Using Linq I would like to return an object that contains customers and invoices they have.
I understand returning a single type from a method:
public IQueryable<customers> GetCustomers()
{
return from c in customers
select c;
}
But I am having trouble figuring out multiple objects:
public IQueryable<???> GetCustomersWithInvoices()
{
return from c in customers
from inv in c.invoices
select new {c, ci} // or I may specify columns, but rather not.
}
I have a feeling I am approaching this the wrong way. The goal is to call these objects from a controller and pass them up to a view, either direct or using a formViewModel class.
In the second case you are creating an annonymous type which has method scope. To pass an annonymous type outside the method boundary you need to change the return type to object. This however defeats the purpose of the annonymous type (as you lose the strong typing it provides) , requiring reflection to get access to the properties and their values for the said type.
If you want to maintain this structure as your return type you should create a class or struct consisting of properties to hold the customer and invoice values.
You cannot return an anonymous type from a function, they are strictly "inline" classes. You will need to create a concrete type to hold your members if you want to encapsulate them in a function.
Using a view model, as you mentioned, would be a good place to put them.
Here is a scottgu article about anonymous types. From the conclusion of the article:
Anonymous types are a convenient
language feature that enable
developers to concisely define inline
CLR types within code, without having
to explicitly provide a formal class
declaration of the type. Although
they can be used in lots of scenarios,
there are particularly useful when
querying and transforming/shaping data
with LINQ.
There's some good discussion in the comment thread on that page.
If you really want to, you can do this, but it is rather awkward.
public IQueryable<T> GetCustomersWithInvoices(T exampleObject)
{
return from c in customers
from inv in c.invoices
select new {c, ci} // or I may specify columns, but rather not.
}
var exampleObject = new {
Customer c = new Customer(),
Invoice i = new Invoice()
};
var returnedObjectOfAnonymousType = GetCustomersWithInvoices(exampleObject);
In this way, you can take advantage of type inference to get your method to return an anonymous type. You have to use this ugly method of passing in an example object to get it to work. I don't really recommend that you do this, but I believe that this is the only way to do it.