I'm trying to build my first app on CodeIgniter. This is also my first time trying to stick to OOP and MVC as much as possible. It's been going ok so far but now that I'm trying to write my first model I'm having some troubles. Here's the error I'm getting:
A Database Error Occurred
Error Number: 1064
You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'Castledine' at line 3
SELECT * FROM (authors) WHERE author = Earle Castledine
Which, as you'll see below, relates to the following line in my model:
$this->db->get_where('authors', array('author' => $author));
I'm not quite sure why it's throwing the error. Is it because Earle Castledine isn't in quotes? If so, why doesn't CI put them in there? I would doubt that's the issue, rather to think it's my fault, but I'm not sure.
I'm having another issue as well. Neither tags nor authors are getting inserted into their respective tables. Their insert statement is wrapped in a conditional that's supposed to be making sure they don't already exist, but it seems to be failing and the insert never happens. I assume it's failing because the tags aren't getting put in the database and it's down in the author section before it tosses the error. I know how to do this with pure PHP but I'm trying to go about doing it the pure CI ActiveRecord way.
Here's the statement I'm using:
if ($this->db->count_all_results() == 0)
And I'm using that instead of what I'd normally use:
if (mysql_num_rows() == 0)
Am I doing it wrong?
Here are my model and controller (only the functions that matter), commented as best I could.
Model:
function new_book($book, $tags, $authors, $read) {
// Write book details to books table
$this->db->insert('books', $book);
// Write tags to tag table and set junction
foreach ($tags as $tag) {
// Check to see if the tag is already in the 'tags' table
$this->db->get_where('tags', array('tag' => $tag));
// trying to use this like mysql_num_rows()
if ($this->db->count_all_results() == 0) {
// Put it there
$this->db->insert('tags', $tag);
}
// Set the junction
// I only need the id, so...
$this->db->select('id');
// SELECT id FROM tags WHERE tag = $tag
$query = $this->db->get_where('tags', array('tag' => $tag));
// INSERT INTO books_tags (book_id, tag_id) VALUES ($book['isbn'], $query->id)
$this->db->insert('books_tags', array('book_id' => $book['isbn'], 'tag_id' => $query->id));
}
// Write authors to author table and set junction
// Same internal comments apply from tags above
foreach ($authors as $author) {
$this->db->get_where('authors', array('author' => $author));
if ($this->db->count_all_results() == 0) {
$this->db->insert('authors', $author);
}
$this->db->select('id');
$query = $this->db->get_where('authors', array('author' => $author));
$this->db->insert('authors_books', array('book_id' => $book['isbn'], 'author_id' => $query));
}
// If the user checked that they've read the book
if (!empty($read)) {
// Get their user id
$user = $this->ion_auth->get_user();
// INSERT INTO books_users (book_id, tag_id) VALUES ($book['isbn'], $user->id)
$this->db->insert('books_users', array('book_id' => $book['isbn'], 'user_id' => $user->id));
}
}
Controller:
function confirm() {
// Make sure they got here by form result, send 'em packing if not
$submit = $this->input->post('details');
if (empty($submit)) {
redirect('add');
}
// Set up form validation
$this->load->library('form_validation');
$this->form_validation->set_error_delimiters('<h3 class="error">', ' Also, you’ll need to choose your file again.</h3>');
$this->form_validation->set_rules('isbn','ISBN-10','trim|required|exact_length[10]|alpha_numeric|unique[books.isbn]');
$this->form_validation->set_rules('title','title','required');
$this->form_validation->set_rules('tags','tags','required');
// Set up upload
$config['upload_path'] = './books/';
$config['allowed_types'] = 'pdf|chm';
$this->load->library('upload', $config);
// If they failed validation or couldn't upload the file
if ($this->form_validation->run() == FALSE || $this->upload->do_upload('file') == FALSE) {
// Get the book from Amazon
$bookSearch = new Amazon();
try {
$amazon = $bookSearch->getItemByAsin($this->input->post('isbn'));
} catch (Exception $e) {
echo $e->getMessage();
}
// Send them back to the form
$data['image'] = $amazon->Items->Item->LargeImage->URL;
$data['content'] = 'add/details';
$data['error'] = $this->upload->display_errors('<h3 class="error">','</h3>');
$this->load->view('global/template', $data);
// If they did everything right
} else {
// Get the book from Amazon
$bookSearch = new Amazon();
try {
$amazon = $bookSearch->getItemByAsin($this->input->post('isbn'));
} catch (Exception $e) {
echo $e->getMessage();
}
// Grab the file info
$file = $this->upload->data();
// Prep the data for the books table
$book = array(
'isbn' => $this->input->post('isbn'),
'title' => mysql_real_escape_string($this->input->post('title')),
'date' => $amazon->Items->Item->ItemAttributes->PublicationDate,
'publisher' => mysql_real_escape_string($amazon->Items->Item->ItemAttributes->Publisher),
'pages' => $amazon->Items->Item->ItemAttributes->NumberOfPages,
'review' => mysql_real_escape_string($amazon->Items->Item->EditorialReviews->EditorialReview->Content),
'image' => mysql_real_escape_string($amazon->Items->Item->LargeImage->URL),
'thumb' => mysql_real_escape_string($amazon->Items->Item->SmallImage->URL),
'filename' => $file['file_name']
);
// Get the tags, explode by comma or space
$tags = preg_split("/[\s,]+/", $this->input->post('tags'));
// Get the authors
$authors = array();
foreach ($amazon->Items->Item->ItemAttributes->Author as $author) {
array_push($authors, $author);
}
// Find out whether they've read it
$read = $this->input->post('read');
// Send it up to the database
$this->load->model('add_model', 'add');
$this->add->new_book($book, $tags, $authors, $read);
// For now... Later I'll load a view
echo 'Success';
}
}
Could anyone help shed light on what I'm doing wrong? Thanks much.
Marcus
Where you are using count_all_results(), I think you mean to use num_rows(). count_all_results() will actually create a SELECT COUNT(*) query.
For debugging your problems...
If you want to test if an insert() worked, use affected_rows(), example:
var_dump($this->db->affected_rows());
At any point you can output what the last query with last_query(), example:
var_dump($this->db->last_query());
You can also turn on the Profiler so you can see all the queries being run, by adding in the controller:
$this->output->enable_profiler(TRUE);
I managed to figure this out on my own. The controller didn't really change, but here's the new model:
function new_book($book, $tags, $authors, $read) {
// Write book details to books table
$this->db->insert('books', $book);
// Write tags to tag table and set junction
foreach ($tags as $tag) {
// Check to see if the tag is already in the 'tags' table
$query = $this->db->get_where('tags', array('tag' => $tag));
// trying to use this like mysql_num_rows()
if ($query->num_rows() == 0) {
// Put it there
$this->db->insert('tags', array('tag' => $tag));
}
// Set the junction
// I only need the id, so...
$this->db->select('id');
// SELECT id FROM tags WHERE tag = $tag
$query = $this->db->get_where('tags', array('tag' => $tag));
$result = $query->row();
// INSERT INTO books_tags (book_id, tag_id) VALUES ($book['isbn'], $query->id)
$this->db->insert('books_tags', array('book_id' => $book['isbn'], 'tag_id' => $result->id));
}
// Write authors to author table and set junction
// Same internal comments apply from tags above
foreach ($authors as $author) {
$query = $this->db->get_where('authors', array('author' => mysql_real_escape_string($author)));
if ($query->num_rows() == 0) {
$this->db->insert('authors', array('author' => mysql_real_escape_string($author)));
}
$this->db->select('id');
$query = $this->db->get_where('authors', array('author' => mysql_real_escape_string($author)));
$result = $query->row();
$this->db->insert('authors_books', array('book_id' => $book['isbn'], 'author_id' => $result->id));
}
// If the user checked that they've read the book
if (!empty($read)) {
// Get their user id
$user = $this->ion_auth->get_user();
// INSERT INTO books_users (book_id, tag_id) VALUES ($book['isbn'], $user->id)
$this->db->insert('books_users', array('book_id' => $book['isbn'], 'user_id' => $user->id));
}
}
Related
The problem is when I entered a new name no data is added. A similar thing happen when I entered an already existing name. Still, no data is added to the database. I am still new to CodeIgniter and not entirely sure my query builder inside the model is correct or not.
In the Model, I check if the name already exists insert data only into the phone_info table. IF name does not exist I insert data into user_info and phone_info.
Controller:
public function addData()
{
$name = $this->input->post('name');
$contact_num = $this->input->post('contact_num');
if($name == '') {
$result['message'] = "Please enter contact name";
} elseif($contact_num == '') {
$result['message'] = "Please enter contact number";
} else {
$result['message'] = "";
$data = array(
'name' => $name,
'contact_num' => $contact_num
);
$this->m->addData($data);
}
echo json_encode($result);
}
Model:
public function addData($data)
{
if(mysqli_num_rows($data['name']) > 0) {
$user = $this->db->get_where('user_info', array('name' => $data['name']))->result_array();
$user_id = $user['id'];
$phone_info = array(
'contact_num' => $data['contact_num'],
'user_id' => $user_id
);
$this->db->insert('phone_info',$phone_info);
} else {
$user_info = array(
'name' => $data['name']
);
$this->db->insert('user_info', $user_info);
$user = $this->db->get_where('user_info', array('name' => $data['name']))->result_array();
$user_id = $user['id'];
$phone_info = array(
'contact_num' => $data['contact_num'],
'user_id' => $user_id
);
$this->db->insert('phone_info', $phone_info);
}
}
DB-Table user_info:
DB-Table phone_info:
Extend and change your model to this:
public function findByTitle($name)
{
$this->db->where('name', $name);
return $this->result();
}
public function addData($data)
{
if(count($this->findByTitle($data['name'])) > 0) {
//.. your code
} else {
//.. your code
}
}
Explanation:
This:
if(mysqli_num_rows($data['name']) > 0)
..is not working to find database entries by name. To do this you can use codeigniters built in model functions and benefit from the MVC Pattern features, that CodeIgniter comes with.
I wrapped the actual findByName in a function so you can adapt this to other logic and use it elswehere later on. This function uses the query() method.
Read more about CodeIgniters Model Queries in the documentation.
Sidenote: mysqli_num_rows is used to iterate find results recieved by mysqli_query. This is very basic sql querying and you do not need that in a MVC-Framework like CodeIgniter. If you every appear to need write a manual sql-query, even then you should use CodeIgniters RawQuery methods.
How do get the related user table of a newly created Contact model and then in the response header content-length out put it toJson().
public function store(Request $request) {
try {
$contact = new Contact();
$contact->email_address = Helper::strip_tags($request->get('email_address'));
$contact->firstname = ucfirst($request->get('firstname'));
$contact->lastname = ucfirst($request->get('lastname'));
$contact->company = ucfirst($request->get('company'));
$contact->phone = $request->get('phone');
$contact->mobile = $request->get('mobile');
$contact->description = Helper::strip_tags($request->get('description'));
if($contact->save()) {
// here is the part I'm having trouble with
$contact = $contact->with('user')->get();
return response()->json($contact, 200, ['Content-Length' => strlen($contact->toJson())]);
} else {
return response()->json(array('error' => true, 'messages' => $contact->errors), 400);
}
} catch(Exception $e) {
return response()->json(array('error' => true, 'type' => 'exception', 'message' => $e->getMessage()), 500, ['Content-Length' => $e->getMessage()]);
}
As you already have the model loaded (when you created it) you wouldn't use with() as it is for eager loading relationships.
If I understand you question correctly, to get the User relationship included in the output you would instead use lazy eager loading which would look like:
$contact->load('user');
when i use following method and pass body key as fail (non defined key) and some value getting pass message in return and empty row gets inserted in table, How do I validate?
Function that I use in REST API,
function categories_POST() {
$title = $this->post('title');
$no = $this->post('no');
$id= $this->post('id');
$this->load->model('model_check');
$msg = $this->model_check->addDetails($title , $no , $id);
$this->response($msg);
}
My model,
function addDetails($x, $y, $z) {
$check = "INSERT INTO categories (title,no,id) VALUES ('$x','$y','$z')";
$query = $this->db->query($check);
if($this->db->affected_rows() > 0) {
return "pass";
} else {
return "fail";
}
}
quite honestly, you'd be better off using the query builder and (depending on what style you follow(fat/skinny controllers/models)) letting the model deal with $this->post() for processing.
Is this Phil Sturgeons/Chris A's rest server?
Something like:
function categories_post() { // doesn't need to be POST()
$this->load->model('model_check');
$msg = $this->model_check->addDetails()
if ($msg)
{
$this->response([
'status' => TRUE,
'message' => 'pass'
], REST_Controller::OK);
}
// default to fail
$this->response([
'status' => FALSE,
'message' => 'fail'
], REST_Controller::HTTP_BAD_REQUEST);
}
Your model,
function addDetails() {
// this only checks to see if they exist
if (!$this->post() || !$this->post('x') || !$this->post('y') || !$this->post('z')) {
return false;
};
$insert = array(
'x' => $this->post('x'),
'y' => $this->post('y'),
'z' => $this->post('z'),
);
if($this->db->insert('categories', $insert))
{
return true;
}
return false; // defaults to false should the db be down
}
IF you mean form_validation you can use this instead of the above.
function addDetails() {
$this->load->library('form_validation');
$this->form_validation->set_rules('x', 'X', 'required');
$this->form_validation->set_rules('y', 'Y', 'required');
$this->form_validation->set_rules('z', 'Z', 'required');
if ($this->form_validation->run() == true)
{
$insert = array(
'x' => $this->post('x'),
'y' => $this->post('y'),
'z' => $this->post('z'),
);
if($this->db->insert('categories', $insert))
{
return true;
}
}
return false; // defaults to false should the db be down
}
This is quite verbose, there's shorter ways to do it, but I'd rather make it easy to figure out.
Two ways of get post values in CodeIgniter
$title = $this->input->post('title');
$no = $this->input->post('no');
$id= $this->input->post('id');
$this->load->model('model_check');
$msg = $this->model_check->addDetails($title , $no , $id);
$this->response($msg);
or
extract($_POST);
Then direct access post name
$this->load->model('model_check');
$msg = $this->model_check->addDetails($title , $no , $id);
$this->response($msg);
Best way is directly access post values in model files (not in controller)
Don't need the pass the POST values in model function.
If you have more queries, then ask to me
I used Yii 2.
To be clear:
The two tables, content and task, is 1:1 relation, task.content_id =
content.id;
and I referenced
doc.
in view file, I used Gridview to show data.
and I wanna the content is also searchable even it is in another
table.
Maybe the following sql can explain what I want:
SELECT
c.content,
t.publish_status
FROM
content c, task t
WHERE
c.content LIKE '%keywordInContent%' AND
t.publish_status = 1 AND
c.id = t.content_id
ORDER BY
updated_at
LIMIT 20;
Here is my controller code:
public function actionIndex()
{
$searchModel = new TaskSearch;
$dataProvider = $searchModel->search(Yii::$app->getRequest()->get());
return $this->render('index', [
'dataProvider' => $dataProvider,
'searchModel' => $searchModel,
]);
}
and search model code:
public function search($params)
{
$query = Task::find()->trashed(Task::TRASHED_NO);
$dataProvider = new ActiveDataProvider([
'query' => $query,
]);
if (!($this->load($params) && $this->validate())) {
return $dataProvider;
}
$query->andFilterWhere(['publish_status' => $this->publish_status]);
return $dataProvider;
}
I have settled it by searching like clause before and adding the resulted content_id to searchModel query, code like follow:
if (!empty($this->keyword)) {
$contentIdArr = Content::find()->select('id')->where(['like', 'content', $this->keyword])->column();
if (empty($contentIdArr)) {
$contentIdArr = -1;
}
$query->andFilterWhere(['content_id' => $contentIdArr]);
}
I wonder is there a way to construct the sql I wrote at begin in Yii 2 ?
thanks for help.
Could you try this:
public function search($params)
{
$query = Task::find()->trashed(Task::TRASHED_NO);
$dataProvider = new ActiveDataProvider([
'query' => $query,
]);
if (!($this->load($params) && $this->validate())) {
return $dataProvider;
}
if(!empty($this->keyword))
$query->leftJoin('content', 'content.id=task.content_id')
->andWhere(['publish_status' => 1])
->andWhere(['LIKE', 'content', $this->keyword])
->orderBy('updated_at');
return $dataProvider;
}
But the updated_at shouldn't be part of the search, I guess. This is more about sorting. Look here for an example.
I think you can do it by first getting Content data and have a relation with Task model.
You can achieve relation by this link. Relation in Content model:
public function getTask()
{
return $this->hasOne(Task::className(), ['content_id' => 'id']);
}
and search query
$searchResult = Content::find()
->select('content, publish_status')
->with('task')
->where(['LIKE', 'content', 'keywordInContent'])
->andWhere(['publish_status' => 1])
->orderBy('updated_at')
->limit(20)
->all();
I think this might help you.
I have been doing frontend magento for a while but have only just started building modules. This is something i know how to do frontend but i am struggling with in my module. What i am trying to achieve for now, is populating a multiselect in the admin with all available product attributes. Including custom product attributes across all product attribute sets. I'm not entirely sure what table this will require because i don't want to assume that Flat Category Data is enabled.
I have created my admin area in a new tab in system config, i have created a multiselect field that is currently just being populated with three static options. This much works. Could anyone help me by pointing a finger in the right direction... currently this is what i have so far (for what it's worth).
<?php
class test_test_Model_Source
{
public function toOptionArray()
{
return array(
array('value' => 0, 'label' =>'First item'),
array('value' => 1, 'label' => 'Second item'),
array('value' => 2, 'label' =>'third item'),
);
}
}
///////////////////////////// EDIT /////////////////////////////////////
I feel like i might be onto something here, but it's only returning the first letter of every attribute (so i'm not sure if its even the attributes its returning)
public function toOptionArray()
{
$attributes = Mage::getModel('catalog/product')->getAttributes();
$attributeArray = array();
foreach($attributes as $a){
foreach($a->getSource()->getAllOptions(false) as $option){
$attributeArray[$option['value']] = $option['label'];
}
}
return $attributeArray;
}
///////////////////////////////// EDIT //////////////////////////////////////
I am not extremely close as i now know that the array is returning what i want it to, all attribute_codes. However it is still only outputting the first letter of each... Anyone know why?
public function toOptionArray()
{
$attributes = Mage::getModel('catalog/product')->getAttributes();
$attributeArray = array();
foreach($attributes as $a){
foreach ($a->getEntityType()->getAttributeCodes() as $attributeName) {
$attributeArray[$attributeName] = $attributeName;
}
break;
}
return $attributeArray;
}
I have answered my own question. I have found a way that worked however i'm not sure why, so if someone could comment and explain that would be useful. So although having $attributeArray[$attributeName] = $attributeName; worked when it came to a print_r when you returned the array it was only providing the first letter. However if you do the following, which in my opinion seems to be doing exactly the same thing it works. I can only imagine that when rendering it wasn't expecting a string but something else. Anyway, here is the code:
public function toOptionArray()
{
$attributes = Mage::getModel('catalog/product')->getAttributes();
$attributeArray = array();
foreach($attributes as $a){
foreach ($a->getEntityType()->getAttributeCodes() as $attributeName) {
//$attributeArray[$attributeName] = $attributeName;
$attributeArray[] = array(
'label' => $attributeName,
'value' => $attributeName
);
}
break;
}
return $attributeArray;
}
No need to do additional loops, as Frank Clark suggested. Just use:
public function toOptionArray()
{
$attributes = Mage::getResourceModel('catalog/product_attribute_collection')->addVisibleFilter();
$attributeArray = array();
foreach($attributes as $attribute){
$attributeArray[] = array(
'label' => $attribute->getData('frontend_label'),
'value' => $attribute->getData('attribute_code')
);
}
return $attributeArray;
}
You can try to get attributes in other way, like this
$attributes = Mage::getSingleton('eav/config')
->getEntityType(Mage_Catalog_Model_Product::ENTITY)->getAttributeCollection();
Once you have attributes you can get options in this way (copied from magento code)
$result = array();
foreach($attributes as $attribute){
foreach ($attribute->getProductAttribute()->getSource()->getAllOptions() as $option) {
if($option['value']!='') {
$result[$option['value']] = $option['label'];
}
}
}