I'm making a project where a user can publish/post their own stories and read others' stories. Very simple.
This is my controller method named publish:
public function published()
{
$story = array('author' => $this->session->userdata('username'),
'title' => $this->input->post('title'),
'synopsis' => $this->input->post('synopsis'));
$new_storyid = $this->story_model->new_story($story);
if($new_storyid != NULL)
{
$genre = $this->input->post('genre');
for($temp=0;$temp<count($genre);$temp++)
{
$genres[$temp] = array('story_id' => $new_storyid,
'story_genre_name' => $genre[$temp]);
}
$insert_genre = $this->story_model->new_story_genre($genres);
$tag = $this->input->post('tags');
for($temp=0;$temp<count($tag);$temp++)
{
$tags[$temp] = array('story_id' => $new_storyid,
'story_tag_name' => $tag[$temp]);
}
$content_warning = $this->input->post('content_warning');
for($temp=0;$temp<count($content_warning);$temp++)
{
$content_warnings[$temp] = array('story_id' => $new_storyid,
'story_content_warning_name' => $content_warning[$temp]);
}
//$chapter = array('story_id' => $new_storyid,
//'chapter_number' => 1, 'chapter_title' => $this->input->post('chapter_title'),
//'chapter_content' => $this->input->post('chapter_content'),
//'chapter_number' => 1, 'date_added' => mdate('%Y-%m-%d %h-%i-%s',time()));
//$result = $this->story_model->add_chapter($chapter);
//if($result){
//redirect('account/userprofile_published_stories');}
}
}
This is my model methods for the above controller method:
public function new_story($story)
{
$this->db->select('user_id');
$query = $this->db->get_where('users',array('username' => $story['author']))->result();
foreach($query as $row)
{ $userid = $row->user_id; }
$publish = array('user_id' => $userid,
'story_title' => $story['title'],
'synopsis' => $story['synopsis']);
$this->db->insert('story',$publish);
return $this->db->insert_id();
}
public function new_story_genre($genre)
{
foreach($genre as $row)
{
$this->db->insert('story_genre', $row);}
}
public function add_chapter($chapter){
$this->db->where('story_id', $chapter['story_id']);
return $this->db->insert('chapters', $chapter);
}
I haven't added the other 2 functions for my tags and content warning inserts because i am confused right now. It all works fine, my genre is inserted.
My tables looks like this:
Story tables
In inserting a story in my above method, the first thing i do is insert a new story row in my story table and returns the new_storyid variable.
after that with the new storyid i add the genre,tags,content warning then the chapters.
My question is, what should i return in my methods for inserting the genre,tags,contentwarning?
I forgot this part because every model method ive written so far always returns a variable i needed in my controller. My first thought was to return a TRUE/FALSE variable if insert is successful/fail but barring special circumstances since ive already processed the data its 100% sure to insert successfully. Should i be returning TRUE/FALSE and adding an if statement like:
if($insert_genre){
//insert tags here
if($insert_tags){
//insert content warning here
if($insert_content_warning){
//insert chapters here
//redirect to view here
}
}
}
Or can i just not return anything? and if so, is this a proper/right way?
EDIT: I forgot to mention i haven't yet added form_validation rules before all the inserts. So my function will be nested in multiple if statements.
I just edited my model method:
public function new_story_genre($genre){
$inserted = 0;
foreach($genre as $row){
$this->db->insert('story_genre', $row);
$inserted += $this->db->affected_rows();}
if($inserted == count($genre)){
return TRUE;}else{ return FALSE; }
}
Above compares the number of inserted rows with the number of rows passed into the method. Everytime a row is inserted it adds 1 to the inserted variable. So if my controller passes 3 rows into the method, the inserted variable should also be 3 for a successful insert.
I think you are correct in always returning something. Errors can and do happen for whatever reason, and its a good idea to account for them even if you already validated your data (you never know). Coding practices suggest that more than a couple of nested ifs is bad practice. A personal preference of mine is to check for failure rather than success all the way down the chain until the last lines of the function (if it got that far than everything is good to go).
A scheme like this I usually use:
public function something() {
if (!$insert_genre) {
// add flash error message
// redirect to controller
}
if (!$insert_tags) {
// add flash error message
// redirect to controller
}
if (!$insert_content_warning) {
// add flash error message
// redirect to controller
}
// yay, something went right!
}
In this kindof circumstance it is very procedural. The most important conditions should be first, and if C depends on A, then A should be the first condition.
Unrelated:
It is hard to follow some of your text here, but it also seems like you should look into how you are doing the genres. If the entered genre already exists in the database do you really need to add it? Shouldn't you just use a relationship there storing the id in the main table and joining when displaying?
I have reached very strange problem using Zend Framework 1 and binding the resultset to BvbGrid.
We have a method which queries an external API. You need to specify the following params: methodName, params.
So the controller recieves a resultset as an array from the external API.
The resultset (in the database) is 4 columns - 1 string, 2 DateTime, 3 int, 4 int. And is with about 3000 rows.
When I recieve the resultset in the controller I bind it to Bvb Grid, which should evaluate in the view as columns with search boxes.
So far, so good, when I search in the first or in the last column (string, int), it searches in the whole resultset.
But when I search 2, 3 (DateTime, int) it searches only in the current page.
I don't understand why is that. There is no difference in data binding, or something.
The code is as follows
public function indexAction() {
$pagesize = $this->_request->getPost('rows');
$pagenum = $this->_request->getPost('page');
$pagesize = ($pagesize) ? $pagesize : 30;
$pagenum = ($pagenum) ? $pagenum : 1;
$params = array();
$params['page_num'] = $pagenum;
$params['page_limit'] = $pagesize;
if ($this->_request->isXmlHttpRequest()) {
// additional search filter
if ($this->_request->getPost('username')) {
$params['username'] = $this->_request->getPost('username');
}
if ($this->_request->getPost('lastlogin')) {
$params['lastlogin'] = $this->_request->getPost('lastlogin');
}
if ($this->_request->getPost('type')) {
$params['type'] = $this->_request->getPost('type');
}
if ($this->_request->getPost('level')) {
$params['level'] = $this->_request->getPost('level');
}
}
if (($order_by = $this->_request->getPost('sidx')) && ($order_type = $this->_request->getPost('sord'))) {
$params['order_by'] = $order_by;
$params['order_by_type'] = $order_type;
}
$resultset = $this->web_service->_apiRequest('SearchUsers', $params);
/**
* The resultset is like
* array(
'found_rows' => 3000,
'body' => array(
'username' => 'blabla',
'lastlogin' => '2014-02-25 13:33:38.1234',
'type' => 1,
'level' => 199
)
);
*/
$this->view->count = $resultset['found_rows'];
if ($resultset['found_rows'] > 0 || $this->_request->isXmlHttpRequest()) {
$grid = new Bvb_Grid_Deploy_JqGrid($this->_gridconfig);
$grid->setSource(new Bvb_Grid_Source_Array($resultset['body']));
$grid->getSource()->setTotalRecords($resultset['found_rows']);
$grid->updateColumn('username', array('width' => '110',));
$grid->updateColumn('lastlogin', array('width' => '110',));
$grid->updateColumn('type', array('width' => '110',));
$grid->updateColumn('level', array('width' => '110',));
$grid->setJqgParams(array('caption' => 'resultset'
, 'forceFit' => true
, 'viewrecords' => true
, 'rowList' => array(15, 30, 50)
, 'altRows' => false
)
);
$grid->ajax(get_class($grid));
$grid->setNumberRecordsPerPage(30);
$grid->setExport(array());
$this->view->grid = $grid->deploy();
}
I have tried to examine the BvbGrid's code, but found nothing strange.
In another project the same code is used, and it works fine, without any problems on searching in any column. I have dump'd the API response to be sure it gives me 3000 records, and it actually is and they are in the way as in the comment. I really cannot find the reason why i.e. the lastlogin column searches only on the current page.
If someone can help me, or give me directions where to look at to fix the issue (maybe somewhere in the BvbGrid's code?) I will appreciate it.
Thank you in advance.
I have a model called Game, which has a hasMany relationship with a Planet model (which in turn has a belongsTo defined back to the Game model).
Upon creating my first Game record, I use the value of its starting_num_planets field to try to create that many associated Planet records. The GamesController does this by calling a function in the PlanetsController which, each time called, returns an array that matches the Planet model, and should be saveable, which is the next thing the GamesController tries to do.
The Game record is created just fine, but I am somehow getting an invalid SQL syntax error apparently at the point at which I'm trying to access the second controller's function. Which is weird, because I'm not trying to save until a couple of lines later.
Here's the code from the GamesController:
public function newgame() {
if ($this->request->is('post')) {
$this->Game->create();
if ($this->Game->save($this->request->data)) {
// Add the 'id' element to the Game array and put the whole game record on the session
$this->request->data['Game']['id'] = $this->Game->getLastInsertID();
$this->Session->write('Game', $this->request->data);
// Create the number of planet records indicated by start_num_planets
$count = 0;
$planets = array();
while ($count < $this->request->data['Game']['starting_num_planets']) {
$planetData = $this->Game->Planet->generate_planet_data($this->request->data['Game']['id']);
$planets[$count] = $planetData;
$this->Game->Planet->create();
$this->Game->Planet->save($planetData);
$count++;
}
$this->redirect(array('action' => 'main'));
} else {
$this->Session->setFlash('There was a problem creating the game.');
}
}
}
At the line number for this line:
$planetData = $this->Game->Planet->generate_planet_data($this->request->data['Game']['id']);
I am getting:
Error: SQLSTATE[42000]: Syntax error or access violation: 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 'generate_planet_data' at line 1
SQL Query: generate_planet_data
And I have no idea why, I'm afraid.
Here's the function being called in the PlanetsController:
public function generate_planet_data($gameId) {
$planetNames = array(1 => 'name1', 2 => 'name2', 3 => 'name3', 4 => 'name4');
$planetImages = array(1 => '01', 2 => '02', 3 => '03', 4 => '04');
$planetData = array();
// game_id is the foreign key, do I have to explicitly state that in the model?
$planetData['Planet']['game_id'] = $gameId;
$planetData['Planet']['planet_name'] = $planetNames[rand(1,10)];
$planetData['Planet']['planet_image_file'] = $planetImages[rand(1,4)];
return $planetData;
}
The model association in Game.php is simply:
var $hasMany = array(
'Planet' => array(
'className' => 'Planet'),
'Player' => array(
'className' => 'Player'
)
);
Can anyone tell what's going wrong? This is my first attempt at using associated models, and it's gone a bit awry! :)
The problem is that you're calling a missing function here:
$planetData = $this->Game->Planet->generate_planet_data($this->request->data['Game']['id']);
As you said generate_planet_data is on the controller. So you should call it thusly:
$planetData = $this->generate_planet_data($this->request->data['Game']['id']);
As stated in the comments, this logic probably belongs in the Planet model. If you moved it there you would then be able to access it via your original way.
I'm starting out using the Zend Framework and have created a suitable model which saves data back to a database table. The issue I am having is that the sql statement is trying to insert '?' as the value for each column in the database. I have created the following save function which passes an array of data to the DBtable adapter functions:
public function save() {
$data = $this->getData();
if ($data['pageId']==0) {
$this->getDbTable()->insert($data);
} else {
$this->getDbTable()->update($data, array('pageId = ?' => $data['pageId']));
}
}
This seems to go through the appropriate motions but the item is not added to the database and the sql statement within MySql logs looks something like:
insert into DB_Table ('pageId','title','body') values ('?', '?', '?');
Not quite sure where this is falling down, any pointers would be gratefully received.
Thanks
Data should be in next format:
$data = array(
'pageId' => 1,
'title' => 'title',
'body' => 'body'
);
Are you sure that $this->getDbTable() returns your db adapter?
Try to use:
$db = new Zend_Db_Table('table_name');
$db->insert($data);
Please, read this question carefully before answer or pressing "close" link.
This question is about sharing experience, exchange of tips and tricks.
What code you are using to insert data into mysql database?
Just a snip of code from your real project. Preferably of CRUD type.
I mean real code. Please, do not copy-paste code examples from manual. It's different from the real life needs. Please, do not answer "you can use these methods...". I know them all. I am asking not for methods but for real coding experience.
I find it very interesting and very enlightening to share your code, to learn from others experience.
Please note that code must be complete, including all data preparations. But no validation if possible. A backstage work can be omitted if there is too much of it (like model initializing and such). I am asking more for food for thought than for code to copy and paste.
Please, do not close this topic too fast.
I an hungry for real world code examples, there is a very little of them here, but dummy code snippets everywhere.
Languages other than PHP are welcome, as well as any ORM or framework usage. But please, remember - not copy-paste from documentation example, but from your own project. There is huge difference.
There's the Redbean ORM. What I have done is basically wrap my own code around its domain object, hence it looks like
class Book extends RedBean_DomainObject
{
public function __construct($id)
{
if ($id!=0)
$this->find($id);
}
public function save_details($author, $title)
{
// insert OR update new entry
$this->author = $author;
$this->title = $title;
$this->save();
}
}
The code will check if the 'bean' exists; if it does, it will load it. You assign properties to the class, and call the save() method to save it to the bean. The RedBean ORM will automatically detect if it is a save or an update.
Note: The RedBean Domain Object has been replaced by something better, though.
I am also using WordPress wp-db, and I like the syntax
$wpdb->insert("books", array('title' => $title, 'author' =>$author));
I found a little wrapper online which allows me to do INSERT...ON DUPLICATE KEY too.
$wpdb->insert_on_duplicate("author_book_relationship",
array('book_id' => $book_id,
'date_published' =>$date_published),
array('book_id' => $book_id));
The first parameter is the table, the second is the insert/update information and the last one is the where clause for the UPDATE part.
Edit
I usually wrap SQL functions in a helper
class BookHelper
{
public function save_relationship($id, $book, $author)
{
global $wpdb;
$wpdb->insert_on_duplicate("author_book_relationship",
array('book_id' => $book_id,
'date_published' =>$date_published),
array('book_id' => $book_id));
}
}
And inside a strategy
class BookSaveStrategy
{
protected $book_helper;
public function save_relationship($id, $book, $title)
{
// validate id, book and title
//.....
// Save if ok
$this->book_helper->save_relationship($id, $book, $title);
}
}
Which can be use in a controller
if (isset($_POST['save_book']))
{
$book_save_strategy->save($_POST['id'], $_POST['author'], $_POST['title']);
}
My framework's ORM:
$User = new User();
$User->name = 'John';
$User->email = 'john#example.com';
$User->homepage = 'http://example.com';
$User->save();
Keeping it simple, lightweight, and effective.
Using ADOdb:
$stmt = $db->Prepare("INSERT INTO `table` (`column1`, `column2`, `column3`) VALUES (?, ? ,?)");
$insert = $db->Execute($stmt, array($value1, $value2, $value3));
if($insert === false) throw new Exception($db->ErrorMsg());
Below are some examples from our company's framework.
Raw queries:
Db::query(
"UPDATE sometable SET city = %s, region = %s WHERE id = %d",
$city, $region, $id);
// or
Db::query(
"UPDATE sometable SET city = %(city)s, region = %(region)s WHERE id = %(id)d",
array('city' => $city, 'region' => $region, 'id' => $id));
There is a number of placeholders supported, such as %s (string), %d (integer), %f (float), %? (auto-detects type of the parameter), %- (no escape, insert as is) and even %as, %ad etc. (arrays of strings/integers/whatever, replaced with comma-separated values). Everything is properly escaped behind the scene.
There is also some kind of ORM with quite limited features:
$city = !empty($id) ? City::fetchOneById($id) : City::create();
$city->setValues(array('city' => $city, 'region' => $region));
$city->save();
Function taken from a recent project's model (using Codeigniter). It's used to
insert a period of vacation in the calendar and in the table that keeps track
of variations (minus for vacations taken, plus is system added every month).
DateIterator is a custom iterator which returns consecutive dates in the format
yyyy-mm-dd, work_days is a custom helper that count the - ugh - number of
working days between two supplied dates. Apologies for italian variable names.
function richiesta_ferie($utente, $inizio, $fine) {
// INSERT INTO Ferie
$data = array(
'ID_Utente' => $utente,
'Richiesta' => date('Y-m-d H:i:s'),
'Inizio' => $inizio,
'Fine' => $fine,
'Stato' => 'P',
);
$this->db->insert('Ferie', $data);
$ID_Ferie = $this->db->insert_id();
// INSERT INTO Variazione
$work_days = -1 * work_days($inizio, $fine);
$data = array(
'ID_Richiesta' => $ID_Ferie,
'ID_Utente' => $utente,
'Giorni' => $work_days,
);
$this->db->insert('Variazione', $data);
// INSERT INTO Giorno
// DateIterator defined in helpers/MY_date_helper.php
$DateIterator = new DateIterator($inizio, $fine);
foreach ( $DateIterator as $date ) {
// skip sundays
if ( date('w', strtotime($date)) == 0 ) {
continue;
}
$data = array(
'ID_Utente' => $utente,
'ID_Richiesta' => $ID_Ferie,
'TipoRichiesta' => 'F',
'Giorno' => $date,
'Stato' => 'P',
);
$this->db->insert('Giorno', $data);
}
}
try {
$pSt = $dbh->prepare('INSERT INTO voucher (field1, field2, field3) VALUES (:field1, :field2,:field3)');
$pSt->execute(array(':field1'=>$field1, ':field2' =>$field2,':field3'=>$field3));
$status=$pSt->errorCode();
if($status=='00000'){
echo "Voucher $voucher created successfully.";
}else{
$error=$pSt->errorInfo();
echo $error[2];
}
} catch (Exception $e) {
echo "Failed: " . $e->getMessage();
}
Changed field names.Otherwise this is the code that I use.
Here is an example from my one of my scripts:
$fields=array('city','region');
if ($id=intval($_POST['id'])) {
$query="UPDATE $table SET ".dbSet($fields)." WHERE id=$id";
} else {
$query="INSERT INTO $table SET ".dbSet($fields);
}
dbget(0,$query);
dbSet() is a helper function to produce an SQL SET statement out of $_POST array and array contains field names, using a consideration which makes form field names equal to table field names.
dbget() is a function to run a query, 0 means raw type of return value, in case it will be needed in the further code.
public static function insertSQL($table, $fields, $values)
{
if (self::$_databaseLogger == null) { self::$_databaseLogger = &LoggerManager::getLogger('databaseAccess'); }
$fieldCount = count($fields);
$valueCount = count($values);
if ($fieldCount != $valueCount) {
self::$_databaseLogger->error('database.insertSQL() Error: field list is different size to value list');
throw new Exception("Database::insertSQL field list is different size to value list");
} else {
$sql = 'INSERT INTO '.$table.' ('.implode(', ',$fields).') VALUES ('.implode(', ', $values).')';
self::$_databaseLogger->databaseQuery('database.insertSQL(): '.$sql);
$statement = database::getDBConnection()->prepare($sql);
$insertedRows = $statement->execute();
if ($insertedRows === False) {
self::$_databaseLogger->error('database.insertSQL(): insertSQL ERROR');
throw new Exception('database::insertSQL ERROR');
}
self::$_databaseLogger->databaseResult('database.insertSQL(): Inserted '.$insertedRows.' rows');
return $insertedRows;
}
} // function insertSQL()
All data values are validated, quoted where appropriate for strings and escaped prior to calling the insertSQL() method. The logger used is log4PHP.
EDIT
Use case:
$fileTreeTableFieldsArray = array ( 'FILE_ID',
'FILE_TYPE',
'FILE_NAME',
'FILE_PARENT_ID',
'FILESIZE',
'CREATED_BY',
'CREATED_ON',
'APPLICATION_CONTEXT' );
$fileTreeTableValuesArray = array ( database::getSQLValueString($this->_fileID,'int'),
database::getSQLValueString($fileType,'text'),
database::getSQLValueString($name,'text'),
database::getSQLValueString($parentID,'int'),
database::getSQLValueString($fileSize,'int'),
database::getSQLValueString($_SESSION["USERID"],'int'),
database::getSQLValueString('sysdate','datetime'),
database::getSQLValueString($_SESSION["APPLICATION"],'int') );
Database::startTransaction();
try {
$result = database::insertSQL('RCD_FILE_TREE',
$fileTreeTableFieldsArray,
$fileTreeTableValuesArray);
} catch (Exception $e) {
Database::rollback();
$error = $this->_setError(baseFileClassInsertException, $this->_fileCategory, $this->_fileName, $sql, $e->getMessage());
$this->_logger->error($this->_ErrorMessage);
return $error;
}
Database::commitTransaction();