I have encountered something very, very weird. Working in VirtualBox, Ubuntu, Apache2, PHP, MySQL I got some very strange behaviour from one particular model.
I wanted to add ability to enter multiple language versions of the same string, say product name. I used jQuery to make nice tabs with languages and created temporary array to store all that information, used CActiveForm widget to collect and present data.
<?php foreach($languages as $language): ?>
<div id="tab_<?=$language->code?>">
<div class="row">
<?php echo $form->labelEx($model,'name'); ?>
<?php echo $form->textField($model,"translations[$language->code][name]",array('size'=>60,'maxlength'=>128)); ?>
<?php echo $form->error($model,"translations[$language->code][name]"); ?>
</div>
</div>
[...]
So this is how I collect data into a $translations array. This is my $translations array:
$translations = array(
'name' => 'NewName',
'sub_name' => 'Subname'
);
Then I obviously assign it to a proper model in Controler action:
[...]
foreach ($translations as $key => $value){
$x = new Translations();
$x->language = $key;
$x->id = $product->id;
$x->name = $value['name'];
$x->sub_name = $value['sub_name']
$x->save();
}
[...]
Now there are also other fields that are only one per product:
[...]
<div class="row">
<?php echo $form->labelEx($model,'something'); ?>
<?php echo $form->textField($model,'something',array('size'=>60,'maxlength'=>128)); ?>
<?php echo $form->error($model,"something"); ?>
</div>
</div>
And those are stored simply by
$product->save();
Everything is in a neat transaction.
HOWEVER....
What I get in result is like this (join on translations and product table):
id name subname something
1 NewN
2 NewName S
3 NewName Subname Som
4 NewName Subname Something
4 records when I only add ONE.... and more text I put in, more records are created. Split by random number of characters, sometimes as little as 4 sometimes as much as 12. This is repeatable but not always.... I am totaly dumbstruck by this behaviour.
Anyone ever saw anything like this and can shed ANY light on it?
Thanks in advance!
The problem could be coming in at many stages:
the form
the controller
binding the data from the form to the model
the model saving (ActiveRecord)
or even the database
So 'divide and conquer' your problem.
MySQL
Turn on your 'general mysql log' to see the queries that are being sent to MySQL. If they look like the weird forms you are seeing above, then it's not the database. It's unlikely you'll have any problems at this stage, but it's good to rule it out.
ActiveRecord
Then hardcode your values into the model. In your controller, just create an instance of the model and save it. Does that replicate the problem you're having? If so, it's a problem with your implementation of ActiveRecord. Try removing the relationships and work on that.
Controller
There could be something weird in your controller. Again, hardcode the data that you want to pass to the model when you create the instance.
Binding
It could be a problem with how you are binding data from the form to the model. Manually set the values of the form, then bind that data to the model as normal.
This is where back-end testing can really help you, as you can isolate what's working from what's not. Look into tools like Behat and PHPUnit.
Related
This is Controller.php
public function dropdown($id)
{
$stations = Station::lists('station_name','id');
return view('stations.show')->withStation($station);
return view('stations.dropdown',compact('stations', 'selectedStation','selectedStation'));
}
This is the view.php
{{ Form::select('station',$stations,null,['class' => 'form-control']) }}
<br>
<?php
if(isset($_GET['submit'])){
$selectedStation = $_GET['station'];
echo $selectedStation;echo "<br>";
}
else
{
echo "not working";
}
?>
corresponding Database table.
This gives me a drop-down like in the below image.
When a station is selected it displays only "id" of the selected station. How to retrieve other columns of the selected station ?.
Many ways that you can tackle this and I have also made some assumptions.
Your list (it would seem) only contains the name & the id.
I assume that you don't want to reload the page when you get the information for the specific item that you have chosen in the dropdown.
Here are the first 2 options that come to mind.
1.) AJAX - Use javascript framework like jQuery and monitor change() for the select, on change get the id and fire it to another route in Laravel which takes the ID and returns some more information in JSON. You can then use JS to display this to your users as you please.
2.) Preloaded JSON - Write new DB Query that gets all the information that you want and store it in JSON format inside some <script> tags. It wont be viewable to the user. Then you can use jQuery or other JS framework to grab that data again on change()
Part 2 will be faster, and if you have got 100,000s records i would advise that for perf.
Apologies if I totally misunderstood the question, it seems like you need a JS solution rather than Laravel/PHP solution. (Without a bad UX) if you did wan't more data you would need to post it and the fetch data from DB on new request.
To display all the columns of all stations you can do the following.
Please note, doing this you fetch all which may not be necessary and there may be better methods available on the eloquent model class depending on what you are trying to do.
Controller
public function showTable()
{
//Fetch all stations from the database
$stations = Station::all();
//Return fetched data to the view
return view('stations.show', ['stations' => $stations]);
}
View - stations/view.blade.php
<ul>
#foreach ($stations as $station)
<li>{{$station->station_name}} - {{$station->rainfall}}</li>
#endforeach
</ul>
For more information and examples about the eloquent model class you can check: https://laravel.com/docs/5.2/eloquent
Simple question but no solution yet. As we know
<?php $form = ActiveForm::begin(['method'=>'get']); ?>
<?= $form->field($formFilter, 'keyword')
->textInput(['placeholder' => \Yii::t('', 'keyword')]); ?>
...
will create simple form and input fields. Of course we will load $_POST data in action like
if ($this->isPost() && $formFilter->load($this->post())) {
if ($formFilter->validate()) {
...
If we will look in $_POST we will see something like FormFilter[keyword] as name of field. So question is, how can I change it? I need (i think) somehow change in in form\model not in view, because we need proper loading in action.
Where it will be used? Any GET form will show ugly url with class names, for example using simple action and models we will get FormFilter[keyword] but I want change it to keyword, so url will be more understandable than 'long field names'.
Anyone know how to deal with this?
Sorry, later I found solution, I think it will help not just me...
Simple one is to redefine formName() method in our form/model. Using formName() we can even change it what ever we need or disable at all if will set such one
public function formName()
{
return '';
}
So, if forName() returns empty string we will get url :
http://site/items?keyword=&locationID=&employmentType=&educationLevel=&salaryMin=
Default one will be:
http://site/items?FormVacanciesFilter[keyword]=&FormVacanciesFilter[locationID]=6&FormVacanciesFilter[employmentType]=&FormVacanciesFilter[educationLevel]=&FormVacanciesFilter[salaryMin]=
You can change it per field in the view, e.g. I have a form based on yii\base\DynamicModel where I need to control the field names, and, for example:
echo $form->field($model, 'test')->hiddenInput(['name' => 'test'])->label(false);
will output:
<div class="form-group field-dynamicmodel-test">
<input type="hidden" id="dynamicmodel-test" class="form-control" name="test" value="{value of $model->test}">
<p class="help-block help-block-error"></p>
</div>
I'm new to AngularJS and decided to try it out. As I read about it that I can simply plug into existing HTML and it will work. And now I'm stuck at this point.
Here is my shortened PHP code with wordpress and angularjs:
<?php
$the_query = new WP_Query(array('post_type' => 'products', 'nopaging' => 'true'));
<div class="container">
<?php while ($the_query->have_posts()) : $the_query->the_post(); ?>
<?php $name = get_field("name"); ?>
<?php $tree = get_field("tree"); ?>
<div class="material" ng-click="productMaterial(product)"
ng-init="product={name:'<?php echo $name; ?>',tree:'<?php echo $tree; ?>'};">
</div>
<?php endwhile; ?>
</div>
After the loop is done, then according to HTML source, all the ng-inits <div class="material">-s get the correct values:
<div class="material" ng-click="productMaterial(product)" ng-init="product={name:'Castello',tree:'oak'};"></div>
..
<div class="material" ng-click="productMaterial(product)" ng-init="product={name:'Variostep CL',tree:'oak'};"></div>
My controller:
calculator.controller('Calc1ProductController', ['$scope', 'calc1Service', function($scope, calc1Service) {
$scope.productMaterial = function(product) {
console.log("Clicked product: " + $scope.product.name); // print always Variostep CL
};
}]);
So the problem is that every time I click on any product, always the last product gets printed in console that is Variostep CL. I understand each new cycle in loop just overrides the previous product object and only the last product is stored.
How is it possible to pass the product data so, that whenever i click in webpage, the correct product data is logged to console?
As I have seen that other approach is to use pure angularjs approach, that I do the API request and receive JSON and then use instead of WP loop the ng-repeat, but then it will require me to rewrite even more code.
I was thinking to make an incrementing counter and store array of products and then access by the index in angular controller or maybe is there some simpler approach?
If my question is too messy, I can bring webpage snapshots and more clearer explanation. Thank you
Quoting from http://docs.angularjs.org/api/ng.directive:ngInit
The only appropriate use of ngInit is for aliasing special properties of ngRepeat, as seen in the demo below. Besides this case, you should use controllers rather than ngInit to initialize values on a scope.
But, since you are using Angular with PHP, I am not sure this suggestion still stands.
I think the reason why you are always seeing the last product is because you are continuously overriding the same piece of data in your controller with the ng-init declarations looped by the PHP code you have. In effect, your controller has actually defined $scope.product through your use of ng-init if I am not mistaken. So, all your ng-click calls will use the shared $scope.product as their arguments.
As a suggestion, if it is possible for you to acquire all your product data at once (i.e. as an array), you should do that instead, store the data to your controller, then make use of ng-repeat to have Angular create HTML for each piece of product data.
I am doing a small application. For that Ihave two table like sles and stores
The sales table looks like this
============
Sales
============
id
store_id
Stores
=============
id
store_name
store_location
store_code
description
I have done the model and CRUD for both tables. In stores table I have entered some vales as per the table.
Now in sales controller I have rendered both sales and stores. so here my action create looking like this
public function actionCreate()
{
$model=new Sales;
$stores = new Stores;
// Uncomment the following line if AJAX validation is needed
// $this->performAjaxValidation($model);
if(isset($_POST['Sales'], $_POST['Stores']))
{
$model->attributes=$_POST['Sales'];
$stores->attributes=$_POST['Stores'];
$valid = $model->validate();
$valid = $stores->validate();
if($valid)
{
$stores->save(false);
$model->store_id = $stores->getPrimaryKey();
$model->save(false);
$this->redirect(array('view','id'=>$model->id));
}
}
$this->render('create',array(
'model'=>$model,
'stores'=>$stores,
));
}
and in sales(_form.php) is like this
<div class="row">
<?php echo $form->labelEx($stores,'store_name'); ?>
<?php echo $form->textField($stores,'store_name',array('size'=>60,'maxlength'=>80)); ?>
<?php echo $form->error($stores,'store_name'); ?>
</div>
<div class="row">
<?php echo $form->labelEx($stores,'store_location'); ?>
<?php echo $form->textField($stores,'store_location',array('size'=>45,'maxlength'=>45)); ?>
<?php echo $form->error($stores,'store_location'); ?>
</div>
<div class="row">
<?php echo $form->labelEx($stores,'store_code'); ?>
<?php echo $form->textField($stores,'store_code',array('size'=>45,'maxlength'=>45)); ?>
<?php echo $form->error($stores,'store_code'); ?>
</div>
Now here when I am doing a sales I want that when I will enter one store name by entering keys then it will start to show the related stores names and the store_location and store_code will be auto-fill. Now can someone kindly tell me how to do this? Any help and suggestions will be appreciable. Thanks a lot.
Edit
With this only one field can be autocomplete. But I want all the other related fields should be also autocomplete with this.
A few thoughts first, and then a general answer to your question.
First, a small niggle, you are really only validating the store model, because you reassign $valid right after checking the sale. Instead, the validation logic should be something more like this:
$valid = $model->validate() && $stores->validate();
If either is invalid, it will return false.
Second, I don't think this code is going to do what you want it to do. If you autocomplete the store information based on existing data and submit it back to the Create action, then your code will try to create a brand new store entry based on that information, introducing duplicate stores in your database. If you always created a brand new sale and a brand new store at the same time, this would work, but I'm pretty sure you don't want that.
Instead, to keep it simple this should be an action dedicated to creating only sales objects. Don't try to create a new store object, just start autofilling details into HTML span or div tags instead of into form fields. The only form field will be the field that is the store id, which will be added to your sales object to indicate the foreign key relationship.
Now, to get autofilling going on several fields, I am going to give you an outline and let you fill in the details. You are going to need a new action in your controller, and some Javascript on the page to handle the autofill.
public function actionGetStoreDetails($id) {
// Get the Store model associated with $id
// Create a JSON object based on this model. Hint, check out CJSON::encode()
// Return the result of the above function call.
}
Now you'll have a piece of Javascript on your view page that does something like the following, using jQuery as an example since it's already in Yii.
$(function () {
$('your-store-id-field-html-id-here').keyup(function () {
// Get the current value of the form field
// Make a $.get() request to the new action defined above, passing the ID
// In the callback function, you'll get a JSON object
// Take the elements of the JSON object, like store.store_name, and assign them to <span id="store_name"></span> type fields in your HTML.
});
});
I realize that this is not copy-and-paste code, but I hope it gives you a really good launching pad. You'll learn a ton if you take the time to fill in the blanks and figure out how to do it fully.
I want to show the last login id from the database in view > _form.php file.I have made the code in _form.php file like this
<div class="row">
<?php echo $form->labelEx($model,'id'); ?>
<?php echo Yii::app()->db->getLastInsertId('Form');?>
<?php echo $form->error($model,'id'); ?>
</div>
Here Form is the table and the model name.But Still I am getting ID:0.Where is the wrong part?
All the last_insert_id functions (be they PHP wrappers or the native mySQL one) typically refer to the last ID created using the current database connection. The last login was probably not created during the same request you are showing the table in, so this method won't work for you.
Use a normal SELECT to find out the newest login instead - e.g. by using ORDER by creationtime DESC LIMIT 1.
Related: How to get a highest field value in a table in MySQL?
Pekka's answer is good for common. But if you want to do that action in Yii Framework, try this:
$myModel = new $model;
$model -> savel(false);
echo $model->primaryKey; // Prints the last id.
Or you may try this too for general solution:
Yii::app()->db->getLastInsertID();
Finally, I suggest you to check out this
$max = Yii::app()->db->createCommand()->select('max(id) as max')->from('TABLENAME')->queryScalar();
Just add one to get the next.