How to use a single form to collect data for two or more models in Yii2? - yii

My problem is to dynamically create forms through jquery.
The user should be able to dynamically generate inputs such that he can save multiple rows into the corresponding model/table in one go.
Now I don't know how do I generate the name attribute for the forms for multiple models. I suppose it should be something like ModelName[Property][] (but I would preferably want to do it in 'Yii way' instead of hardcoding the names)
To understand this better, Here I found a similar post in Yii Wiki.
using a single form to collect data for two or more models
How can this be modified for Yii2? So that the user should be able to fill in data for all the (dynamically generated) rows and submit them in one go.

Tutorial of tabular form for multiple models.

Try this, Reference
public function actionCreate()
{
$user = new User;
$profile = new Profile;
if ($user->load(Yii::$app->request->post()) && $profile->load(Yii::$app->request->post()) && Model::validateMultiple([$user, $profile])) {
$user->save(false); // skip validation as model is already validated
$profile->user_id = $user->id;
$profile->save(false);
return $this->redirect(['view', 'id' => $user->id]);
} else {
return $this->render('create', [
'user' => $user,
'profile' => $profile,
]);
}
}

Related

Restrict data for multiple users

I add multiple users as teacher. And I create a table where every teacher update his data that is documents etc.,but when another teacher login he can also view that data how user to restrict to show that to other users?
You need to read more about Yii, and how you can use it.
For your case you can read this: add condition
For your question you can do this in your action in controller:
0) ensure that user is logged in: \Yii::$app->user->isGuest || //redirect to login page or via Access control filters
1) get user id from user. (\Yii::$app->user->identity->id)
2) set this id in teacher document query. like andWhere(['teacher_id' => $userId]);
public function actionViewDoc()
{ $userId = \Yii::$app->user->identity->id;
$model = TeacherDoc::find()->andWhere(['teacher_id' => $userId]);
return $this->render('viewDoc', [
'model' => $model,
]);
}
This will resolve your issue.
UPD: For more advanced solutions you could use:
1) Access control filters
2) RBAC.

How to setup one-to-one relation in yii2 and also other functionalities mentioned below

One User has One Profile(obviously). How do I design the logic behind this?
I have two tables namely "tbl_users" and "tbl_profile".
When I open the profile page, I should be able to see the "Create
Profile" button only if the profile does not exist.
Once the profile has been created by the user, "Create Profile"
button should not appear next time.
Please describe me in detail how the columns of both tables are linked and what changes I have to include in both the models and other changes too.
Thank you.
In your controller you could write something like this:
$user = User::findOne(Yii::$app->user->id);
$profile = $user->profile;
return $this->render('view', ['user' => $user, 'profile' => $profile]);
Then in your view
if(null !== $profile) {
/*show your button here. It's going to work once because...*/
} else {
/*...here you could show form to create your profile*/
}
In your User model use code like this
public function getProfile()
{
return $this->hasOne(Profile::className(), ['id' => 'profile_id']);
}
This is just an example to give you idea.

How to Send Additional Data in Kendo Grid's AJAX Create

I am able to send additional data to a Kendo grid's AJAX Read action using a pattern like so...
#(Html.Kendo().Grid<ModelClass>()
.DataSource(dataSource => dataSource
.Ajax()
.Read(read => read.Action("Read", "Controller").Data("getAdditionalData"))
function getAdditionalData() {
return {
AdditionalData: 'data'
};
}
public ActionResult Read([DataSourceRequest] DataSourceRequest request, String additionalData)
{
}
... but how does one send additional data to the Create action? In my scenario, creation requires information that is not entered by the user, but is available both in the view and as the return from getAdditionalData(). I've tried passing it using the following, but it is not being received by the action method.
.Create(create => create.Action("Create", "Controller").Data("getAdditionalData"))
public ActionResult Create([DataSourceRequest] DataSourceRequest request, ModelClass model, String additionalData)
How is this done? Or how else can additional data be passed to the AJAX create action?
Ref Docs:
http://docs.telerik.com/kendo-ui/getting-started/using-kendo-with/aspnet-mvc/helpers/grid/ajax-binding#pass-additional-data-to-the-action-method
http://docs.telerik.com/kendo-ui/getting-started/using-kendo-with/aspnet-mvc/helpers/grid/ajax-editing
Update with Solution:
As it happens, there cannot be a field name (e.g. "additionalData") duplication between the grid's view model (e.g. "ModelClass") and the client-side data model. That was not apparent in the question as I sanitized the code to post. Not sure if this is happening at the javascript, MVC or Telerik layer.
The solution was to change the client-side data model's field name to be different that the field name in the grid's view model.
Update with Another Solution:
Another option is to set the default value of the field in the grid's view model for create operations like so:
.DataSource(dataSource => dataSource
.Ajax()
.Model(model => {
model.Field(x => x.Id) // Set default value for grid model field
.DefaultValue(Model.DefaultId); // as provided by controller in view model.
})
I've got the same problem and I resolve it.
public ActionResult Create([DataSourceRequest] DataSourceRequest request, ModelClass model, String additionalData)
in this line, change String for string and additionalData for AdditionalData, the same word you have in your js.
One more things, in the clause .Events of DataSource, don't put at the end .RequestEnd("getAdditionalData")
Normally it will work.
Hope it helps!

Typeahead with database as source

This snippet works and it correctly sets data from Table Product and column name as input for bootstrap typeahead extension for YII.
but, I have ended up writing a SELECT ALL from Table Product which is having large number of data.
Can we modify this so that a WHERE condition can be added to the DataProvider on user input event. Based on each alphabet entered, a new query could then be fired and only a subset of data retrieved?
<?php
$dataProvider = new CActiveDataProvider('Product');
$dataArray = $dataProvider->getData();
$myarray = array();
foreach ($dataArray as $data){
array_push($myarray, CHtml::encode($data->name));
}
$this->widget('bootstrap.widgets.TbTypeahead', array(
'name' => 'typeahead',
'options'=>array(
'name'=>'typeahead',
'source'=>$myarray,
'items'=>4,
'matcher'=>"js:function(item) {
return ~item.toLowerCase().indexOf(this.query.toLowerCase());
}",
),
'htmlOptions'=>array('class'=>'search-query span3', 'placeholder'=>"Search" ),
)); ?>
Once you start to supply a function to source, then you have the power to manipulate what happens, including how often you send requests.
minLength: 3, // <- custom option
source: function(query, process) {
var longEnough = query.length >= this.options.minLength;
// you can create custom variables (this.search) that a remembered across
// searches
if (longEnough && (! this.search || whateverRuleYouWantToLimitBy)) {
// remember the query so that you can compare it to the next one
this.search = query;
$.ajax({
url: '/ajaxsearch.php?value=' + query,
type: "GET",
success: process
});
}
}
I have some code that does something similar, and I cache the results returned by the Ajax code, and then I see if the new query string has the potential to change the results (e.g., if you limit by 4 results, but I only have 3 results, then a query that simply adds to the last query (search) has no need to hit the server).
Alternatively, you can kick off a timer that effectively waits for the user to stop typing to avoid the behavior of hitting the server for every key press. Technically, that results in slower feedback, but it's better for the server and mobile users. This is appropriate on sites that have a lot of traffic.

Multiple submit buttons. One formtype

I have a ProfileType form with a user picture field. Is it possible to build two different forms out of the same type and entity on the same page? I want the user to be able to upload an avatar separately by ajax, which I have no problem with, but in the event of no js, I want accessibility and the ability to just use good old php, but in separate forms.
Yes. You need to create two forms with two different names:
$formFactory = $this->container->get('form.factory');
$form1 = $formFactory->createNamed(new MyType(), 'form1', $object);
$form2 = $formFactory->createNamed(new MyType(), 'form2', $object);
if ('POST' === $request->getMethod()) {
if ($request->request->has('form1')) {
$form1->bindRequest($request);
...
}
if ($request->request->has('form2')) {
$form2->bindRequest($request);
...
}
}