I want to insert my notifications static , but I'm checking the variable type is not empty, but at the moment insert says Message: Illegal string offset 'notificationCount' and Undefined index: type. I'm trying to make my array dynamically, but it doesn't seem to work.
public function addNotification($message, $product_id, $type = ''){
$types = array('new' => 0, 'pending' => 1, 'low stock' => 3);
if (isset($types[$type]) === false) {
throw new \InvalidArgumentException('Value for third parameter must be one of new, pending, or low stock.');
}
$type = $types[$type];
$time = time();
$query = "SELECT COUNT(*) AS notificationCount FROM storelte_notifications WHERE product_id = ? AND type = ? ";
$previousNotification = $this->db->query($query, array($product_id, $type));
if ($previousNotification[0]['notificationCount'] == 0) {
$sql = "INSERT INTO storelte_notifications (message,type,product_id,created_at) VALUES(?, ?, ?, ?)";
$this->db->query($sql, array($message, $type, $product_id, $time));
try {
if ($this->db->query($sql)) {
return true;
}else{
return false;
}
} catch (Exception $e) {
}
}else{
return true;
}
}
controller
public function add(){
$this->notification->addNotification('low stock',4228,'type');
}
$sql_prev_notification is a string that you've created here:
$sql_prev_notification = "SELECT COUNT(*) AS notificationCount FROM storelte_notifications WHERE product_id = ? AND type = ? ";
You use it to execute a query with:
$this->db->query($sql_prev_notification, array($product_id, $type));
but you haven't assigned the returned results of the query to anything.
$sql_prev_notification is still a string, so when you do this:
if ($sql_prev_notification[0]['notificationCount'] == 0) {
$sql_prev_notification[0] is referring to the first letter in the string (S), which obviously isn't an array, hence the
Illegal string offset 'notificationCount'
You probably want something more like:
$sql = "SELECT COUNT(*) AS notificationCount FROM storelte_notifications WHERE product_id = ? AND type = ? ";
$sql_prev_notification = $this->db->query($sql, array($product_id, $type));
if ($sql_prev_notification[0]['notificationCount'] == 0) {
although you should also be checking if your query actually returns anything before referring to a specific item from its results.
Don't Panic's answer (https://stackoverflow.com/a/43529550/4132627) is the right one to fix the illegal string offset, go with that.
For the error Undefined index: type, it would appear that you are passing the string "type" as the value for the variable $type. It then uses that value as a key for the $types array, but the $types array doesn't have an index "type" - its indexes are "new", "pending", and "low stock".
To fix this, you have to pass either "new", "pending", or "low stock" as the third parameter to your addNotification function:
$this->notification->addNotification('low stock',4228,'new');
//or
$this->notification->addNotification('low stock',4228,'pending');
//of
$this->notification->addNotification('low stock',4228,'low stock');
You should also check that the key passed is valid, otherwise you will continue to get this notice. In fact, passing the wrong value probably causes your code to function irregularly, in which case throwing an exception is probably a good idea:
if (isset($types[$type]) === false) {
throw new \InvalidArgumentException('Value for third parameter must be one of new, pending, or low stock.');
}
$type = $types[$type];
You can try this:
public function addNotification( $message, $product_id, $type = '' ){
$types = array( 'new' => 0, 'pending' => 1, 'low stock' => 3);
if (isset($types[$type]) === false) {
throw new \InvalidArgumentException('Value for third parameter must be one of new, pending, or low stock.');
}
$type = $types[$type];
$time = time();
$this->db->select_sum('notificationCount');
$this->db->where(['product_id' => $product_id, 'type' => $type]);
$query = $this->db->get();
$previousNotification = $query->row_array()
if ( $previousNotification['notificationCount'] == 0 ) {
$this->db->trans_start();
$this->db->insert(storelte_notifications, ['message' => $message, 'type' => $type, 'product_id' => $product_id, 'created_at' => $time]);
$this->db->trans_complete();
return $this->db->trans_status();
} else {
return true;
}
}
Related
I am using CodeIgniter to build my api and I am trying to find the best way to allow multiple params to be sent to then run my Model where clause if they exist. I am running into some problem and would appreciate if someone could pass on some advise best practise extra I fell like my whole setup is just getting bloated.
My query could take the following params:
/v1/tags?status=1&parentId=1&order=desc&limit=10&offset=1
Here is my table.
id int(11) NO PRI auto_increment
parentId int(11) NO
name varchar(250) NO
status tinyint(4) NO
createdDate timestamp NO CURRENT_TIMESTAMP
Here is my controller.
/**
* READ TAGS
*/
public function tags_get() {
// OPTIONALS:
$parentId = $this->get('parentId');
$status = $this->get('status');
// DEFAULTS:
$offset = $this->get('offset');
$order = $this->get('order');
$limit = $this->get('limit');
// WHERE QUERY:
$where = [];
// VALIDATE:
if(isset($status)){
if ($status != 'publish' && $status != 'future' && $status != 'draft' && $status != 'pending' && $status != 'private' && $status != 'trash') {
$this->response(array(
'status' => FALSE,
'message' => '(status) must be one of the following (publish|future|draft|pending|private|trash)'
), REST_Controller::HTTP_OK);
}
// ADD TO QUERY:
$where['status'] = $status;
}
if(isset($parentId)){
if (filter_var($parentId, FILTER_VALIDATE_INT) === false) {
$this->response(array(
'status' => FALSE,
'message' => '(parentId) must be int'
), REST_Controller::HTTP_BAD_REQUEST);
}
// ADD TO QUERY:
$where['parentId'] = $parentId;
}
// IF NO PARAMS RETUNR ALL DATA
$data = $this->user_model->get_tags($where, $order, $offset, $limit);
if($data){
$this->response([
'status' => TRUE,
'message' => 'Success',
'paging' => $offset,
'records' => count($data),
'data' => $data,
], REST_Controller::HTTP_OK);
}else{
$this->response([
'status' => FALSE,
'message' => 'Not found',
'data' => []
], REST_Controller::HTTP_NOT_FOUND);
}
}
And here is my Model
function get_tags($where = [], $order = 'desc', $offset = 0, $limit = 100){
// MAIN QUERY:
$this->db->select('*');
$this->db->from('tags');
// OPTIONAL WHERE QUERIES:
foreach ($where as $key => $value) {
$this->db->where($key, $value);
}
// DEFUALTS:
$this->db->order_by('createdDate', $order);
$this->db->limit($limit, $offset);
$query = $this->db->get();
return ($query->num_rows() > 0) ? $query->result_array() : FALSE;
}
Take the following Query.
/v1/tags?status=0
This fails should I be using YES | NO or ON | OFF as varchars in my database instead of booleans?
UPDATE:
Based on Rays answer I will be changing status to accept the following values.
publish
future
draft
pending
private
trash
I have also update my controller see above.
To be honest, your approach is quite good, better you go with ON OFF choice as numbers might get complicated if you want to adapt new states,
Let's take this example,
For some reason, your Manager added 3 more stats to your system, lets say 0,1,2,3,4
which means
0 is off
1 is on
2 is pending
3 is damaged
4 is cancelled
you will not be able to remember status based on their number in the future, but if you use names instead, you can understand better.
In the end, for sake of stability, stick with known structure.
I want to to once the user has sold an item, but if my final stock if less than my min stock then it will insert a notification on my table from database, but after that push with node.js that notification, but when I try to insert to my table after sale show me an error like this how should I fix it?
public function concretar_venta(){
if($this->sale->checa_carrito_vacio($this->session->carrito)){
$total = $this->input->post("total", TRUE);
$cantidad_pagada = $this->input->post("cantidad_pagada", TRUE);
$cambio = $cantidad_pagada - $total;
if($this->sale->concretar_venta($this->session->carrito, $total, $cantidad_pagada, $cambio)){
$this->json(array('success' => 'The sale was successfully made'));
}
else{
$this->json(array('error' => 'There was an error making the sale, please try again'));
}
$this->session->carrito = $this->sale->checar_existe_carrito();
$array = $this->sale->get_all_cart($this->session->carrito);
$product_id = array();
foreach ($array as $key => $value) {
$product_id[] = $value['id'];
}
$this->notification->addNotification('low stock', $product_id, $this->session->log['id'], 'low stock');
/*if ($product->stock <= 8) {
$this->notification->addNotification('low stock', $product_id, $this->session->log['id'], 'low stock');
} else {
# code...
}*/
}
else{
$this->json(array('error' => 'The cart is empty'));
}
}
model notification:
public function addNotification($message, $product_id, $user_id, $type = ''){
$types = array('new' => 0, 'pending' => 1, 'low stock' => 2);
if (isset($types[$type]) === false) {
throw new \InvalidArgumentException('Value for third parameter must be one of new, pending, or low stock.');
}
$type = $types[$type];
$timestamp = time();
$query = "SELECT COUNT(*) AS notificationCount FROM storelte_notifications WHERE product_id IN ? AND type = ? ";
$previousNotification = $this->db->query($query, array($product_id, $type))->result_array();
if ($previousNotification[0]['notificationCount'] == 0) {
$sql = "INSERT INTO storelte_notifications (message,type,product_id,user_id,timestamp) VALUES(?, ?, ?, ?, ?)";
try {
foreach ($product_id as $pid) {
if (!$this->db->query($sql, array($message, $type, $pid, $user_id, $timestamp))) {
return false;
}
}
return true;
} catch (Exception $e) {
}
}else{
return true;
}
}
error output:
Error Number: 1064 You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near ') AND type = 2' at line 1 SELECT COUNT(*) AS notificationCount FROM storelte_notifications WHERE product_id IN () AND type = 2 Filename: C:/xampp/htdocs/storelte/system/database/DB_driver.php Line Number: 691
IN() requires a string of product_ids to check in, so you need to convert your array of product_ids to a string
between these lines:
$timestamp = time();
$query = "SELECT COUNT(*)...
add
$timestamp = time();
$product_id = implode(',',$product_id);//add this line
$query = "SELECT COUNT(*)...
http://php.net/manual/en/function.implode.php
This question already has answers here:
PHP PDO Insert Using Loop
(2 answers)
Closed 5 months ago.
foreach($parentinfojson as $value) {
if (!empty($parentinfojson )) {
$stmt2 = $dbh -> prepare("INSERT INTO parentinfo (last_name,first_name,status) VALUES (:lastname,:firstname,:status)");
$stmt2_ = $stmt2 -> execute(array(':firstname' => $value['firstname'], ':lastname' => $value['lastname'], ':status' => $status));
} else {
$stmt2_ = $stmt2 -> execute();
}
}
if ($stmt2_ && $stmt3_ && $stmt1_ && $stmt_ && $stmt5_ && $stmt4_) {
echo json_encode(array(
'error' => false,
'message' => "Added"
));
}
This is my execute in Inserting new data in the table. When i tested the adding of empty data(parentinfojson is empty) i get error that Notice: Undefined variable: stmt2_. What i did is i added an else statement and i initialize the variable still i get error. I tried to echo something in the else statement as well but i get error. Now I run out of idea on how to initialize the variable when the json is empty so that i dont get the error undefined variable
You just defined $stmt2 inside the loop, if $parentinfojson is empty it'll certainly get undefined. Why not define/initialize it.
// initialize up top
$stmt_ = $stmt1_ = $stmt2_ = $stmt3_ = $stmt4_ = $stmt5_ = false;
$stmt2 = $dbh->prepare("INSERT INTO parentinfo (last_name,first_name,status) VALUES (:lastname,:firstname,:status)");
foreach($parentinfojson as $value) {
$stmt2_ = $stmt2->execute(array(
':firstname' => $value['firstname'],
':lastname' => $value['lastname'],
':status' => $status
));
}
if ($stmt2_ && $stmt3_ && $stmt1_ && $stmt_ && $stmt5_ && $stmt4_) {
echo json_encode(array(
'error' => false,
'message' => "Added"
));
}
Sidenote: Another way would be to build the query dynamically, including the placeholders and the values. So that in turn, you don't have to loop each batches of insert but instead, creating the SQL batch insert then binding all of the values into one single insert invocation:
$stmt_ = $stmt1_ = $stmt2_ = $stmt3_ = $stmt4_ = $stmt5_ = false;
if(!empty($parentinfojson)) {
$base_query = 'INSERT INTO parentinfo (last_name, first_name, status) VALUES ';
$placeholders = implode(',', array_map(function($batch){
return '(' . implode(',', array_fill(0, count($batch), '?')) . ')';
}, $parentinfojson));
$base_query .= $placeholders;
$parentinfojson = call_user_func_array('array_merge', array_map('array_values', $parentinfojson));
$stmt2 = $dbh->prepare($base_query);
$stmt2_-> $stmt2->execute($parentinfojson);
}
There might be a chance that the json variable you are receiving is not empty, so you should also add a check for valid json, this is the function to check a valid json
function isJson($string) {
json_decode($string);
return (json_last_error() == JSON_ERROR_NONE);
}
I need your help on this php function.
The function takes the data from the db, does the account of the score and exports it. In practice the score must be calculated for both the buyer and for the seller ($type) but when I go to export I only have one of the buyers. The code in question is below. Thanks in advance for the help.
function shop_get_ratings($user_id, $type = 'seller'){
$type = strtolower($type);
$valid = array('seller','buyer');
if( !in_array($type, $valid)){
return false;
}
$conn = getConnection();
$sql = 'SELECT AVG(i_%s_score) as %s_rating FROM %st_shop_transactions WHERE fk_i_user_id = %d AND i_%s_score IS NOT NULL';
$rs = $conn->osc_dbFetchResults(sprintf($sql,$type,$type, DB_TABLE_PREFIX, $user_id, $type));
$seller_r = 0;
if( false !== $rs && isset($rs[0]['seller_rating']) && !is_null($rs[0]['seller_rating']) ){
$seller_r = (int)$rs[0]['seller_rating'];
}
$sql = 'SELECT COUNT(*) as rating_count FROM %st_shop_transactions WHERE fk_i_user_id = %d AND i_%s_score IS NOT NULL';
$rs = $conn->osc_dbFetchResults(sprintf($sql, DB_TABLE_PREFIX, $user_id, $type));
$seller_r_c = 0;
if( false !== $rs && isset($rs[0]['rating_count']) && !is_null($rs[0]['rating_count']) ){
$seller_r_c = (int)$rs[0]['rating_count'];
}
$percentage = 0;
if( $seller_r > 0 ){
$percentage =($seller_r/5)*100;
}
$stats = array(
'average_rating' => (int)$seller_r,
'rating_percentege' => (float)$percentage,
'rating_count' => (int)$seller_r_c,
);
View::newInstance()->_exportVariableToView($type.'_ratings', $stats);
return $stats;
}
From reading the code, it looks like you should get a rating for the seller ok, but it's the buyer who ends up with a 0 rating.
This is because in the line $sql = 'SELECT AVG(i_%s_score) as %s_rating you are inserting $type in to the query to have the field named seller_type or buyer_type, depending on the type of rating you're trying to get with the function.
However when querying the result set you are explicitly looking for the field named seller_rating. This field won't be set when $type is buyer, so $seller_r will always be 0.
The simplest fix here is likely to name the field as something like avg_rating in the sql, with no $type-dependent var name injection. So, something like:
$sql = 'SELECT AVG(i_%s_score) as avg_rating
FROM %st_shop_transactions
WHERE fk_i_user_id = %d
AND i_%s_score IS NOT NULL';
$rs = $conn->osc_dbFetchResults(
sprintf($sql, $type, DB_TABLE_PREFIX, $user_id, $type)
);
$seller_r = 0;
if (false !== $rs
&& isset($rs[0]['avg_rating'])
&& !is_null($rs[0]['avg_rating'])
){
$seller_r = (int)$rs[0]['avg_rating'];
}
Inside my controller, I have a line that needs to pass $content['pass_check'] to the view. It is inside an if statement that checks for validation. This I have found causes it to break. Once I move the $content['pass_check'] outside of any if statement, it works just fine passing to the view. All of the other values are passed (accounts, expense_accounts, vendors, terms). What must I do to get it to pass within this if statement. I've even tried moving it outside of the validation and it still wont set.
function create() {
require_permission("INVOICE_EDIT");
$this->load->library("form_validation");
$this->form_validation->set_rules("invoice_number", "Invoice Number", "required");
if($this->form_validation->run() !== false) {
$post = $this->input->post();
$this->session->set_userdata("create_invoice_vendor", $post['vendor_id']);
$this->session->set_userdata("create_invoice_date", $post['invoice_date']);
$invoice_number_exists = $this->invoices->count(array("invoice_number" => $post['invoice_number'])) > 0;
$post['invoice_date'] = date("Y-m-d", strtotime($post['invoice_date']));
$post['due_date'] = date("Y-m-d", strtotime($post['due_date']));
$post['date_entered'] = "now()";
$id = $this->invoices->insert_invoice($post);
$this->load->model("vendors");
if(isset($post['invoice_number'])){
$string_check= $post['invoice_number'];
$string_check= preg_replace('/\d/', '#', $string_check);
$string_check= preg_replace('/\w/', '#', $string_check);
$invoice_pattern=array();
$invoice_pattern = $this->db->select("invoice_pattern")->where("vendor_id",
$post['vendor_id'])->get("vendors")->result();
$invoice_pattern=$invoice_pattern[0]->invoice_pattern;
* //// THIS IS WHERE I NEED HELP ///////
if($invoice_pattern == $string_check){
***$content['post_check'] = 1;***
$this->invoices->flag_invoice($id);
};
};
$history = array(
"type" => "invoice_entered",
"comments" => "Invoice was entered",
"link" => $id,
"admin_id" => $this->user->admin_id,
"date" => "now()",
);
$this->vendors->insert_history($post['vendor_id'], $history);
if($post['flagged'] == 1) {
$this->invoices->flag_invoice($id);
}
if($invoice_number_exists) {
redirect("invoices/confirm_invoice/".$id);
} else {
// redirect("invoices/view/".$id);
redirect("invoices/create");
}
}
$content['accounts'] = $this->db->get("acct_chart_of_accounts")->result();
$content['expense_accounts'] = $this->db->get("invoice_expense_accounts")->result();
$content['vendors'] = $this->db->select("vendor_id, name, terms, override, invoice_pattern")
->order_by("name ASC")->get("vendors")->result();
$content['terms'] = $this->db->query("SELECT DISTINCT(terms) FROM vendors")->result();
}
}
$this->template['sub_heading'] = "Create";
$this->template['content'] = $this->load->view("invoices/create", $content, true);
$this->template['sidebar'] = $this->load->view("invoices/sidebar", array(), true);
$this->template['scripts'] = array("codeigniter/javascript/invoices/create.js");
$this->template['styles'][] = "codeigniter/styles/invoices/create.css";
$this->display();
}
Obviously it won't pass it to the view if the condition doesn't match, because you're only declaring the variable within the condition if it matches.
Just create $content['pass_check'] with an initial value of 0 or whatever before the conditional check first.
function create() {
...snip...
$content['pass_check'] = 0;
if($invoice_pattern == $string_check) {
$content['post_check'] = 1;
$this->invoices->flag_invoice($id);
};
...snip...
}
Let me know if this works or not please.