I want to store the array data in a database, but in my database I'm getting only the first input tag of the form:
here is my controller:
public function store(Request $request)
{
$qt = Qt::all();
$Itemname = $request->input('Itemname');
$Quantity = $request->input('Quantity');
$Price = $request->input('Price');
$Tax = $request->input('Tax');
$Total = $request->input('Total');
$GrandTotal = $request->input('GrandTotal');
$data = array('Itemname'=>$Itemname,'Quantity'=>$Quantity,'Price'=>$Price,'Tax'=>$Tax,'Total'=>$Total);
dd($data);
Qt::table('qts')->insert($data);
return redirect()->route('quotes.index');
}
I'm not sure what you mean by 'one data' - if you are only storing one field, that shouldn't be happening. The way you have this set up, it is data for one row. Thus, the expected output to the database would be a single Qt model saved. This would be what I would expect in a store() method based on a user entering a single item (with name, price, tax, etc). One form for one new Qt. And I think you have it almost set up for this.
However, Laravel makes the storage of a new model much easier. You don't need pretty much any of that code, because it looks like you have your incoming form fields set to correctly match the name of the fields on the Qt model in the database. Though, you might want to make them lowercase on both to match convention.
If the form fields are a match to the database fields on the model, you can remove almost everything in that method and replace it with:
public function store(Request $request)
{
Qt::create($request->all());
return redirect()->route('quotes.index');
}
The create method will take the $request object as is, and automatically set the right elements to the right fields if they are set to fillable on the model.
EDIT:
I understand you are looking to make multiple rows of items for a single customer based on many items coming in through your form. However, I don't know where that information is coming from -- I don't know where the array of items is within your form (or $request object). This might be your reason why you are having an error in the first place: I don't see how to loop or find multiple items, I just see one item coming in, which would produce one row into the database based on that form. The above code is correct based on what you have said is coming from the form.
However, based on the parameterize() error you mention in the comments, you likely have an array somewhere that is causing the parameter issue, and which would help you to create multiple rows. You don't show what your array is, but you mention that these many items would be attached to a single customer.
Here is a possible way to do this. I will make up a few variables based on what you've said (some array to loop on, that there is a 'customer' object or similar, etc.)
public function store(Request $request)
{
$customer = Customer::find($request->get('customer_id'));
// I don't know how you bring in the customer's id - maybe as a $request item or perhaps through GET?
$newItemsArray = []; // Store the rows of new items here
// Whatever the array of items is, pull it and loop on it to create rows of items:
foreach($request->get('someItemsArray') as $newItem){
// Assuming the name of the array fields match the database field names:
// Assumes $newItem is an associative array
$newItemsArray['Itemname'] = $newItem['Itemname'];
$newItemsArray['Quantity'] = $newItem['Quantity'];
$newItemsArray['Price'] = $newItem['Price'];
$newItemsArray['Tax'] = $newItem['Tax'];
$newItemsArray['Total'] = $newItem['Total'];
}
// Here is where you can create all the rows and attach them at the same time to your customer
$customer->Qts()->createMany($newItemsArray);
return redirect()->route('quotes.index');
}
This will allow for creating multiple rows of items and attach to a customer all at once.
You'll need to fill in the blanks - how are you sending the multiple rows from your form, how are you sending the customer who is adding the items, how to deal with GrandTotal (is it an array item, or is it a single field passed from the form, based on a calculation), etc. But, this will answer your question on the controller side to get multiple rows in for a customer.
Related
I have a custom reporting feature I'm trying to build. To keep it short, the user is presented with options to choose from, those options are used to build an Eloquent query that returns data from the database to the front-end, in the form of an HTML table.
I'd like to switch my HTML table out for a Livewire Datatable. To do this, I need to build columns on the fly, based on what the user selects from the filters.
The datatable:
// $cols contains all the necesarry data from the user's selection to build columns the Livewire Datatable can understand.
public function displayReport($cols)
{
// This processes the data and pushes each new column into $this->cols
foreach($cols as $key => $val) {
$instance = app()->make($val['column']);
$columnName = $val['name'];
$item = $instance->name($columnName);
$item->filterable = true;
$item->hideable = true;
array_push($this->cols, $item);
}
public function columns()
{
return $this->cols;
}
I have dd($this->cols) and it is exactly what is needed to build the datatable. The problem is with the refresh of the component (from what I can tell). Upon submitting the users selection and running the whole thing, I get the following error:
Despite my best troubleshooting efforts, I have not been able to get the table to hydrate properly upon submission of my new columns. I even put a dd($this->cols) right before return $this->cols in the column function, and it returns exactly what is expected. But it won't build the table, and it throws that error seen above.
Help is appreciated. Thank you so much. I know this is a weird and complex one.
What I am trying to do
I want to query a specific set of records using active model like so
$jobModel = Jobs::find()->select('JOB_CODE')->distinct()->where(['DEPT_ID'=>$dept_id])->all();
Then I want to assign a flag attribute to the records in this activerecord based on whether they appear in a relationship table
What I have tried
So in my job model, I have declared a new attribute inAccount. Then I added this function in the job model that sets the inAccount flag to -1 or 0 based on whether a record is found in the relationship table with the specified account_id
public function assignInAccount($account_id){
if(JobCodeAccounts::find()->where(['JOB_CODE'=>$this->JOB_CODE])->andWhere(['ACCOUNT_ID'=>$account_id])->one() == null){
$this->inAccount=0;
}
else{
$this->inAccount = -1;
}
}
What I have been doing is assigning each value individually using foreach like so
foreach($jobModel as $job){
$job->assignInAccount($account_id);
}
However, this is obviously very slow because if I have a large number of records in $jobModel, and each one makes a db query in assignInAccount() this could obviously take some time if the db is slow.
What I am looking for
I am wondering if there is a more efficient way to do this, so that I can assign inAccount to all job records at once. I considered using afterFind() but I don't think this would work as I need to specify a specific parameter. I am wondering if there is a way I can pass in an entire model (or at least array of models/model-attributes and then do all the assignations running only a single query.
I should mention that I do need to end up with the original $jobModel activerecord as well
Thanks to scaisEdge's answer I was able to come up with an alternative solution, first finding the array of jobs that need to be flagged like so:
$inAccountJobs = array_column(Yii::$app->db->createCommand('Select * from job_code_accounts where ACCOUNT_ID = :account_id')
->bindValues([':account_id' => $account_id])->queryAll(), 'JOB_CODE');
and then checking each job record to see if it appears in this array like so
foreach($jobModel as $job){
if(in_array($job->JOB_CODE, $inAccountJobs))
$job->inAccount = -1;
else
$job->inAccount = 0;
}
Does seem to be noticeably faster as it requires only a single query.
I want to fill an empty model and save the model in a blob field for later use. My issue is i can not find how to add anouther row to the empty Model.
this works:
$test = LineItem::model();
$test->item_id = '2';
This does not work
$test->1->item_id = '3';
or
$test->item_id[1] = '3';
i have tried looking in the Yii documentation but i was unable to find an answer.
Thanks
Clarification
Im trying to create a false table using the model of a real table. I'm working on an invoicing system and i don't want to right the line items or invoice body information to the DB until it is "closed". Instead i want to fill the corresponding models that will then be serialized and stored in a BLOB field. Once the invoice is finished the data will be written to the table.
You should use
$test = new LineItem;
instead of
$test = LineItem::model();
for INSERT queries. And after setting properties
$test->save();
And so in every iteration.
I've recently started using Zend Framework (1.8.4), to provide admin tools for viewing the orders of a shopping cart site.
What I'd like to do is to efficiently create multiple model (Zend_Db_Table_Row_Abstract) objects from a single database result row.
The relationship is simple:
an Order has one Customer (foreign key order_custid=customer.cust_id);
a Customer has many Orders.
Loading the orders is easy enough. Using the method documented here:
Modeling objects with multiple table relationships in Zend Framework
...I could then grab the customer for each.
foreach ($orderList as $o)
{
cust = $o->findParentRow('Customers');
print_r ($cust); // works as expected.
}
But when you're loading a long list of orders - say, 40 or more, a pageful - this is painfully slow.
Next I tried a JOIN:
$custTable = new Customers();
$orderTable = new Orders();
$orderQuery = $orderTable->select()
->setIntegrityCheck(false) // allows joins
->from($orderTable)
->join('customers', 'cust_id=order_custid')
->where("order_status=?", 1); //incoming orders only.
$orders = $orderTable->fetchAll($orderQuery);
This gives me an array of order objects. print_r($orders) shows that each of them contains the column list I expect, in a protected member, with raw field names order_* and cust_*.
But how to create a Customer object from the cust_* fields that I find in each of those Order objects?
foreach ($orders as $o) {
$cols = $o->toArray();
print_r ($cols); // looks good, has cust_* fields...
$cust = new Customer(array( 'table' => 'Customer', 'data' => $cols ) );
// fails - $cust->id, $cust->firstname, etc are empty
$cust->setFromArray($cols);
// complains about unknown 'order_' fields.
}
Is there any good way to create an Order and a Customer object simultaneously from the joined rows? Or must I run the query without the table gateway, get a raw result set, and copy each of the fields one-by-one into newly created objects?
Zend_Db doesn't provide convenience methods to do this.
Hypothetically, it'd be nifty to use a Facade pattern for rows that derive from multiple tables. The facade class would keep track of which columns belong to each respective table. When you set an individual field or a whole bunch of fields with the setFromArray() method, the facade would know how to map fields to the Row objects for each table, and apply UPDATE statements to the table(s) affected.
Alternatively, you could work around the problem of unknown fields by subclassing Zend_Db_Table_Row_Abstract, changing the __set() behavior to silently ignore unknown columns instead of throwing an exception.
You can't have an OO interface to do everything SQL can do. There must be some line in the sand where you decide a reasonable set of common cases have been covered, and anything more complex should be done with SQL.
I use this method to assign database row fields to objects. I use setter methods, but this could probably be also done with only properties on object.
public function setOptions(array $options){
$methods = get_class_methods($this);
foreach ($options as $key => $value) {
$method = 'set' . ucfirst($key);
if (in_array($method, $methods)) {
$this->$method($value);
}
}
return $this;
}
I'm using the cacheCounter in CakePHP, which increments a counter for related fields.
Example, I have a Person table a Source table. Person.source_id maps to a row in the Source table. Each person has one Source, and each Source has none or many Person rows.
cacheCounter is working great when I change the value of a source on a person. It increments Source.Person_Count. Cool.
But when it increments, it adds it to the destination source for a person, but doesn't remove it from the old value. I tried updateCacheControl() in afterSave, but that didn't do anything.
So then I wrote some code in my model for afterSave that would subtract the source source_id, but it always did this even when I wasn't even changing the source_id. (So the count went negative).
My question: Is there a way to tell if a field was changed in the model in CakePHP?
To monitor changes in a field, you can use this logic in your model with no changes elsewhere required:
function beforeSave() {
$this->recursive = -1;
$this->old = $this->find(array($this->primaryKey => $this->id));
if ($this->old){
$changed_fields = array();
foreach ($this->data[$this->alias] as $key =>$value) {
if ($this->old[$this->alias][$key] != $value) {
$changed_fields[] = $key;
}
}
}
// $changed_fields is an array of fields that changed
return true;
}
With reference to Alexander Morland Answer.
How about this instead of looping through it in before filter.
$result = array_diff_assoc($this->old[$this->alias],$this->data[$this->alias]);
You will get key as well as value also.
You could use ->isDirty() in the entity to see if a field has been modified.
// Prior to 3.5 use dirty()
$article->isDirty('title');
check the doc: https://book.cakephp.org/3/en/orm/entities.html#checking-if-an-entity-has-been-modified
Edits happen infrequently, so another select before you do the update is no big deal, so, fetch the record before you save, save it, compare the data submitted in the edit form with the data you fetched from the db before you saved it, if its different, do something.
In the edit view, include another hidden field for the field you want to monitor but suffix the field name with something like "_prev" and set the value to the current value of the field you want to monitor. Then in your controller's edit action, do something if the two fields are not equal. e.g.
echo $form->input('field_to_monitor');
echo $form->hidden('field_to_monitor_prev', array('value'=>$form->value('field_to_monitor')));
See if the "save" uses some sort of DBAL call that returns "affected rows", usually this is how you can judge if the last query changed data, or if it didn't. Because if it didn't, the affected rows after an UPDATE-statement are 0.
You can call getAffectedRows() on any model class.
From class Model :
/**
* Returns the number of rows affected by the last query
*
* #return int Number of rows
* #access public
*/
function getAffectedRows() {
$db =& ConnectionManager::getDataSource($this->useDbConfig);
return $db->lastAffected();
}