I'm wondering how can I insert tabular data in Yii.
Of course, I've followed docs in this aspect however there are few differences in my situation.
First of all, I want to save two models, exactly as in the docs article. The main difference is that there might be more that one element for second model (simple one to many relation in database).
I use CHtml to build my forms. I implemented a jQuery snippet to add more input groups dynamically.
I'm unable to show my code now as it's totally messed up and not working currently.
My main question is: how to handle the array of elements for second model in Yii?
Define your two models in controller
$model1= new Model1();
$model2= new Model2();
//massive assignments
$model1->attributes=$_POST['Model1']
$model2->attributes=$_POST['Model2']
//validation
$valid= $model1->validate();
$valid =$valid && $model2->validate();
if($valid){
$model1->save(false);
$model1->save(false);
}
if you want to access fields individually dump your post and you can view the the
post array format or instead of doing massive assignments you can manually assign like this
$model1->field1 =$_POST['Model1']['field1'];
//validation logic
...
if($valid){
$model1->save(false);
$model1->save(false);
}
Related
In my controllers that Gii creates it is common to see the following:
if($model->load(Yii::$app->request->post()) && $model->save()){
//.....do something such as redirect after save....//
}else
{
//.....render the form in initial state.....//
}
This works to test whether a POST is sent from my form && the model that I am specifying has saved the posted information (as I understand it).
I've done this similarly in controllers that I have created myself but in some situations this conditional gets bypassed because one or both of these conditions is failing and the form simply gets rendered in the initial state after I have submitted the form and I can see the POST going over the network.
Can someone explain why this conditional would fail? I believe the problem is with the 'Yii::$app->request->post()' because I have removed the '$model->save()' piece to test and it still bypasses the conditional.
Example code where it fails in my controller:
public function actionFreqopts()
{
$join = new FreqSubtypeJoin();
$options = new Frequency();
$model = new CreateCrystal();
if ($model->load(Yii::$app->request->post()) && $model->save()) {
$model->insertFreqopts();
return $this->redirect(['fieldmap', 'id' => $join->id]);
} else {
return $this->render('freqopts', ['join' => $join, 'options' => $options]);
}
}
My initial thought was that I'm not specifying the correct "$model" in that I'm trying to save the posted data to FreqSubtypeJoin() in this case and the $model is CreateCrystal(); however, even when I change the model in this conditional it still fails. It would be helpful if someone could briefly explain what the method 'load' is actually doing in layman's terms if possible.
The load() method of Model class is basically populating the model with data from the user, e.g. a post query.
To do this it firstly loads your array of data in a form that matches how Yii stores your record. It assumes that the data you are trying to load is in the form
_POST['Model name']['attribute name']
This is the first thing to check, and, as long as your _POST data is actually getting to the controller, is often where load() fails, especially if you've set your own field names in the form. This is why if you change the model, the model will not load.
It then check to see what attributes can be massively assigned. This just means whether the attributes can be assigned en-mass, like in the $model->load() way, or whether they have to be set one at a time, like in
$model->title = "Some title";
To decide whether or not an attribute can be massively assigned, Yii looks at your validation rules and your scenarios. It doesn't validate them yet, but if there is a validation rule present for that attribute, in that scenario, then it assumes it can be massively assigned.
So, the next things to check is scenarios. If you've not set any, or haven't used them, then there should be no problem here. Yii will use the default scenario which contains all the attributes that you have validation rules for. If you have used scenarios, then Yii will only allow you to load the attributes that you have declared in your scenario.
The next thing to check is your validation rules. Yii will only allow you to massively assign attributes that have associated rules.
These last two will not usually cause load() to fail, you will just get an incomplete model, so if your model is not loading then I'd suggest looking at the way the data is being submitted from the form and check the array of _POST data being sent. Make sure it has the form I suggested above.
I hope this helps!
I'm very new to Laravel and php frameworks in general, so sorry if I miss anything basic. I've created a new model called Journey in my application which extends the standard Eloquent model. What I've noticed is that I'm using my Journey model in two different Controllers and I'm duplicating a bit of code because of it.
Essentially, what I'm doing is I'm taking the title of a Journey and I'm formatting it with a custom class to clean the title (convert to lowercase, add hyphens, remove whitespace, etc) so I can append it to my page URLs.
In one controller, I'm calling:
$journey = Journey::find($id);
$journey->cleanURL = Url::clean($journey['name']); // This creates a new element/property with a clean string
And in the other, I'm calling:
$journeys = Journey::all();
foreach ($journeys as $journey) {
$journey->cleanURL = URL::clean($journey['name']);
}
It would be inappropriate to add a fixed field to my database with the cleaned URL because I may change the title (which the cleaned URL is based on) at any time and I'd like the URL to update automatically in this event. However, saying this, I'm repeating myself by calling Url::clean twice.
What I'd like to do is write a method or alter an existing method, so that when I call Journey::all() or Journey::find() or any query-based method, the URL field is already present and filled. I've tried looking through some of the Vendor/Eloquent files, but they just make me confused.
How would I go about doing this?
You can use accessor for this.
Add to your Journey model the following function:
public function getCleanUrlAttribute($value)
{
return Url::clean($this->name);
}
Now you will be able to use:
$journey = Journey::find($id);
echo $journey->clean_url;
I've gotten stuck on something that I believe is down to my in-experience with the many to many relationship.
In my application I am mapping interests to products with a many to many. I wish to create the following scenario whereby the full list of interests is listed under a specific product with check boxes. For every check box that is selected when the form is submitted, a row is added to the InterestProductAssignment table.
In my product controller I call the full list of interests-
$interests = Interest::model()->findAll();
Actually I don't get a lot further than this as my mind is in knots wondering where to proceed from here. What I've tried so far is building an array of InterestProductAssignment objects to match the interests array I've returned above. I've tried passing it to the view and building out the form, but I've gotten myself fairly confused by trying to match the two up and I can't believe I'm using Yii correctly as it's messy.
Is anyone able to outline a solution for this problem that enables me to have a checkbox against every interest that would add a link between the product and interest upon submitting? I'd be interested to see what you'd write in the controller and view.
Just to clarify, this page is the subject of just one product.
EXTENSION
For the supplementary problem I'm having, I'm posting the code I've got, this may also help others implement a similar thing, once we find the mistake.
My relation within my product controller reads like this-
'interests'=>array(self::MANY_MANY, 'Interest', 'interest_product_assignment(interest_id, product_id)')
Controller
public function actionUpdate($id)
{
// loadModel as been adapted to be called "->with('interests')"
$model=$this->loadModel($id);
// Uncomment the following line if AJAX validation is needed
// $this->performAjaxValidation($model);
if(isset($_POST['Product']))
{
$model->attributes=$_POST['Product'];
if($model->save())
foreach($_POST['ProductInterest'] as $i => $interest_id){
$this_interest = new InterestProductAssignment;
$this_interest->product_id = $model->id;
$this_interest->interest_id = $interest_id;
$this_interest->save();
}
$this->redirect(array('view','id'=>$model->id));
}
$this->render('update',array(
'model'=>$model,
));
}
Relevant part of view _form.php
<div class="row">
<?php echo CHtml::checkBoxList('ProductInterest', CHtml::listData($model->interests, 'interest_id', true), CHtml::listData(Interest::model()->findAll(), 'id', 'interest'));?>
</div>
The problem is that the second field in checkBoxList does not seem to properly fill in the check boxes that are already selected. I suspect that the root cause of this is likely a daft mistake. I can't spot it though and I'm not familiar enough with checkBoxList.
Thanks in advance.
Goose,
I think what you are looking for here is the CheckBoxList.
In your form you can use the code
echo CHtml::checkBoxList('Product_Interests', $model->getInterests(), CHtml::listData(Interest::model()->findAll(), 'interest_id', 'interest_title'));
//$model->getInterests() is a method that should returns an array of IDs of all the currently selected interests
Then in your controller you can use the code:
foreach($_POST['Product_Interests'] as $interest_id=>$checked)
if($checked)
$model->addInterest($interest_id); //Add Interest
else
$model->removeInterest($interest_id); //Remove an Interest if it exists
//Note: If you have a model that connects these tables those can be created or destroyed here
How to create a multi-model form in Yii? I searched the entire documentation of Yii, but got no interesting results. Can some one give me some direction or thoughts about that? Any help will be appreciable.
In my expirience i got this solution to work and quickly understandable
You have two models for data you wish collect. Let's say Person and Vehicle.
Step 1 : Set up controller for entering form
In your controller create model objects:
public function actionCreate() {
$Person = new Person;
$Vehicle = new Vehicle;
//.. see step nr.3
$this->render('create',array(
'Person'=>$Person,
'Vehicle'=>$Vehicle)
);
}
Step 2 : Write your view file
//..define form
echo CHtml::activeTextField($Person,'name');
echo CHtml::activeTextField($Person,'address');
// other fields..
echo CHtml::activeTextField($Vehicle,'type');
echo CHtml::activeTextField($Vehicle,'number');
//..enter other fields and end form
put some labels and design in your view ;)
Step 3 : Write controller on $_POST action
and now go back to your controller and write funcionality for POST action
if (isset($_POST['Person']) && isset($_POST['Vehicle'])) {
$Person = $_POST['Person']; //dont forget to sanitize values
$Vehicle = $_POST['Vehicle']; //dont forget to sanitize values
/*
Do $Person->save() and $Vehicle->save() separately
OR
use Transaction module to save both (or save none on error)
http://www.yiiframework.com/doc/guide/1.1/en/database.dao#using-transactions
*/
}
else {
Yii::app()->user->setFlash('error','You must enter both data for Person and Vehicle');
// or just skip `else` block and put some form error box in the view file
}
You can find some examples in these two Yii wiki articles:
Yii 1.1: How to use a single form to collect data for two or more models?
Yii 1.1: How to use single form to collect data for two or more models (CActiveForm and Ajax Validation edition).
You don`t need a multi-model. The right use of the MVC pattern requires a Model that reflects your UI.
To solve it, you'll have to use a CFormModel instead of an ActiveRecord to pass the data from View to Controller. Then inside your Controller you`ll parse the model, the CFormModel one, and use the ActiveRecord classes (more than one) to save in database.
Forms Overview and Form Model chapters in Yii Definitive Guide contains some details and samples.
Another suggestions -
Also we can use Wizard Behavior, It's an extension that simplifies the handling of multi-step forms. In which we can use multi model forms for registration process flow or others.
Demo - http://wizard-behavior.pbm-webdev.co.uk/
Lets say for example I am creating a an online shop. I have a controller called products and within that controller I have a function called create_product. Create_product calls a view that displays a form where users get to enter new products into the database.
When the user fills in the form to create a product, should I send the action back to the create_product controller and handle it with an IF statement? or offload to another function?
Example
<form method="post" action="www.example.dev/products/create_product/add">
//the above form would post back to the original controller
function create_product()
{
if(uri->segment(3) == "add")
{
//call a model to do all the database stuff
}
load->view->create_product_form;
}
Is this the best way to handle this or should I be passing it off to another function?
Don't cram a ton of stuff in one function using the URI segment to filter it. createProduct() can list the products available for creation (in a CRUD format, I assume), and the submission of the form should ping another controller with the POSTed data. Perhaps insertProduct(), where the data is sanitized and sent to the model for insertion to the database.
Separation of concerns! Keep the functions as separate as possible with good descriptors for the names of the functions.
I (personally) would have a function that set the form parameters and "launch" the view with that form, and another function used to validate and call the model to put the values of that form into the database. I believe that is really up to you, but the code would be cleaner if you divide the controller with several functions depending on what they actually do.
I like the way symfony deals with forms & form submission. It is in one function (action)
simplified code:
executeCreate() {
$this->form = new Form()
if($r->isMethod('POST')) {
//handle submission
bind();
save();
}