I want to add record to a table with ActiveRecord.
Here is my code:
$model = new Article();
$model->title = 'test title';
$model->content = 'test content';
$model->category_id = 1;
$model->author_id = 1;
if ($model->validate()) {
$model->save();
}
$model->validate() returns true, but $model->save() returns false.
How to find generated raw sql of $model->save()?
Meanwhile:
$model->save()->rawSql is null and $model->getErrors() returns empty array.
In debug, all queries are logged, but I did not find any insert or update query.
$model->save()->rawSql call can not return null, it must throw an exception that you are trying to access property of non-object. $model->save() returns boolean value - either query executed successfully or not.
If $model->getErrors() returns empty array and query was not executed at all I'm pretty sure that something is wrong with model event handlers, especially beforeSave(), check it, it should not return false. Also check attached behaviors event handlers.
As for getting query. It's useless if it simply was not executed, but if it was, here are some ways to achieve it:
1) Probably the best way. Use debug panel. I also mentioned it here.
2) Look at logs as #robsch adviced.
You can't directly get raw SQL in code with $model->save(), It will call either insert() or update(). If you are interested, here is the part of code for insertInternal():
$values = $this->getDirtyAttributes($attributes);
if (empty($values)) {
foreach ($this->getPrimaryKey(true) as $key => $value) {
$values[$key] = $value;
}
}
$db = static::getDb();
$command = $db->createCommand()->insert($this->tableName(), $values);
if (!$command->execute()) {
return false;
}
If you call $command->rawSql you will get raw sql but you can't do that outside because the command is formed internally.
P.S. This piece of code:
if ($model->validate()) {
$model->save();
}
doesn't make sense because $model->save() will call $model->validate() internally.
This code won't show you exactly the raw sql but you'll get the query pre binding and the params
try {
// this will simulate $model->save();
$builder = $model->getCommandBuilder();
$table = $model->getTableSchema();
$command = $builder->createInsertCommand($table, $model->getAttributes());
$command->_connection->enableParamLogging = true;
$command->execute();
} catch (Exception $e) {
// getText() will print the query with the binding params
// getAttributes() will give you the attributes injected
var_dump($command->getText());
var_dump($model->getAttributes());
die();
}
The result will look like:
"INSERT INTO `fruit` (`order`, `name`, `season`) VALUES (:yp0, :yp1,:yp2)"
array(2) {
["order"] => int(1),
["name"] => null,
["season"] => null
}
Related
I have a dbfunctions class that has the functions to retrieve and write data to database. However, the retrieval doesn't work and I can't understand why it doesn't work.
The read functions looks like this:
public function haeParametreittaTietokannasta($f_sql) {
try {
$this->sql_lause = $this->db->prepare($f_sql);
$this->haku = $this->sql_lause->execute();
} catch (PDOException $ex) {
die("Tapahtui virhe tietoja noudettaessa : " . $ex->getMessage());
}
if(!$this->haku) {
$this->tulos = $this->haku->fetchAll();
return $this->tulos;
} else {
return false;
}
} // END FUNCTION
And this is how I call the function:
$countries = $dabase->haeParametreittaTietokannasta("SELECT * FROM countries");
The query always returns false, and I've tried showing error info and it says: array ( 0 => '00000', 1 => NULL, 2 => NULL, ) (And yes, I have created a new object in the main code.)
I've just started to learn PHP and there might be a simple error I just can't see...
You have the wrong if condition:
if(!$this->haku) {
should be
if($this->haku) {
$this->tulos = $this->sql_lause->fetchAll();
PDOStatement::execute() returns a boolean value. You can't use it for fetchAll().
Okay I have a function called sendQuery which sends your query.
I know how to do it with BindParams, but I can't really think of a way to make it work with bind values inside a execute.
This is the code:
public function sendQuery($query, array $value, $do_fetch)
{
$query_process = $this->db->prepare($query);
if(!$query_process->execute($binds))
{
throw new excpetion ("An error has occured!");
}
$this->insert = $this->db->lastInsertId();
$this->row_count = $query_process->rowCount();
if($fetch == true)
{
return $query_process->fetchAll();
}
}
As you see, it executes with $binds,
Works like (WHERE user = ?), but I want to send queries like this:
(WHERE user = :user) instead of a ' ? ', and multiple of them.
How do I do so?
You have to do exactly the same.
Just get rid of useless code and use consistent variable naming
public function sendQuery($query, array $binds, $do_fetch)
{
$stm = $this->db->prepare($query);
$stm->execute($binds);
$this->insert = $this->db->lastInsertId();
$this->row_count = $stm->rowCount();
if($do_fetch)
{
return $stm->fetchAll();
}
}
$sql = "SELECT * FROM t WHERE c1=:name AND c2=:age";
$param = array ("name" => $name,"age" => $age);
$data = $db->sendQuery($sql, $data, 1);
However, instead of just single function I would create a set of them:
query() to run non-select queries
getOne() preforms select and returns scalar value
getRow() returns a row
getAll returns all rows
it could be extremely handy
Ok so I have 2 classes. Photo class and PhotoMapper class.
Photo class contains all the values and outputs them.
PhotoMapper sets the values to the Photo class, by a assign class, like this:
$query = "SELECT * FROM users";
$query = $this->_pdo->prepare($query);
$query->bindValue(":id", $photo->id());
$query->execute();
$data = $query->fetch();
$photo->assign($data);
And the assign:
public function assign($data){
if (is_array($data)) {
foreach ($data as $name => $value)
{
$this->{'_' . $name} = $value;
}
}
}
Now where would i check for if $query->rowCount() > 0 ?
Should i inside is_array after the foreach, make a $this->rowCount = .. ?
What would be best to perform this check? I would like to check for the rowCount outside of both classes..
$photo = new Photo($albumID, $photoID, $view, $userID);
$photoMapper->select($photo, $view);
Is how it looks outside the classes. How can i check and output error if select(which is the query above) didnt find any rows?
I would need to have 2 queries? One to check, and one to select them? or?..
Well, if you're expecting data from the fetch operation but there isn't any (you could simply find out by checking if $data has any contents), you should throw an Exception. The following code snippet would typically be placed before the fetch:
// ...
$query->execute();
if ( !( $data = $query->fetch() ) ) {
throw new Exception('photo could not be loaded');
}
$photo->assign($data);
// ...
However, if you want the code to continue regardless, you can reverse the if condition, and put the assign call inside the statement;
$query->execute();
if ( ( $data = $query->fetch() ) ) {
$photo->assign($data);
}
Given the following example: (from Zend Quick Start Tutorial btw)
public function save(Application_Model_Guestbook $guestbook)
{
$data = array(
'email' => $guestbook->getEmail(),
'comment' => $guestbook->getComment(),
'created' => date('Y-m-d H:i:s'),
);
if (null === ($id = $guestbook->getId())) {
unset($data['id']);
$this->getDbTable()->insert($data);
} else {
$this->getDbTable()->update($data, array('id = ?' => $id));
}
}
Why do we need the unset there ?
I mean, why do we need to destroy a specific array key, if we haven't declare it before ?
And even more bizarre, where do we declare it anyway?
We can have a look on getDbTable method, but even looking at it, I don't find an answer:
public function getDbTable()
{
if (null === $this->_dbTable) {
$this->setDbTable('Application_Model_DbTable_Guestbook');
}
return $this->_dbTable;
}
And if we look into the setDbTable method, there's no $data anywhere.
public function setDbTable($dbTable)
{
if (is_string($dbTable)) {
$dbTable = new $dbTable();
}
if (!$dbTable instanceof Zend_Db_Table_Abstract) {
throw new Exception('Invalid table data gateway provided');
}
$this->_dbTable = $dbTable;
return $this;
}
I know that Zend Framework will automatically find the id of our table here:
class Application_Model_DbTable_Guestbook extends Zend_Db_Table_Abstract
{
/** Table name */
protected $_name = 'guestbook';
}
But I don't understand if this is related somehow...
I believe I've put all the relevant information. Still, if there's nothing relevant, perhaps I'm missing something here:
(source)
http://framework.zend.com/manual/en/learning.quickstart.create-model.html
Thanks a lot
The code seems to imply that $data['id'] is always set, but it might have an empty value ('' or '0'). The unset is there to prevent the INSERT SQL query from trying to insert every new record with a fixed id of 0 (or to prevent the query from breaking due to invalid SQL syntax, can't tell with just this information) in this case.
Update: After reading it once more, it's obvious that the array $data cannot have its id member set at all (there's no code that might set it). Therefore that line is completely redundant the way the code is written right now. It might be a leftover from a previous version of the code.
Probably id is an autoincrement field. The unset is used to make sure, that the INSERT statement will not use a random id, but null.
I have a static method 'findAll' on a model which basically gets all rows with certain criteria. This method works fine and I can call it using:
$m::findAll();
Where $m is the model name as a variable. I can output this and it returns correct results. However, when assigning this to a variable in the Zend_View object, as:
$this->view->viewvariable = $m::findAll();
I get the error:
Zend_Db_Table_Exception: Too many
columns for the primary key
Any ideas why?
Find all function:
final public static function findAll($where = false, array $options = array()) {
$object = new static();
if (!empty($options)) $options = array_merge($object->options, $options);
else $options = $object->options;
$run = $object->buildDefaultSelect($where, $options);
$rows = $run->fetchAll();
if ($options['asObject'] == true) {
$result = array();
foreach ($rows as $r) {
$class = new static();
$class->setInfo($r);
$result[] = $class;
}
return $result;
} else {
if (count($rows) > 0) return $rows;
else return array();
}
}
Note: This function works fine everywhere apart from when assigning to a view variable. If I run the below (not assigning it to a view variable), it shows the correct array data.
var_dump($m::findAll($module['where'], $module['options']));
exit;
In my view (I have replaced the actual name with viewvariable for the sake of this post):
<?php foreach($this->viewvariable as $item) { ?>
//Do some echoing of data in $item
//Close foreach
I doubt the issue is with Zend_View. It's hard to tell without seeing your code, but my guess is that findAll() is using the Zend_Table_Db find() function incorrectly.
To my knowledge, the only place that throws that exception is the find() function on Zend_Db_Table_Abstract.
Perhaps, inside the findAll() function (or in a function it calls) you're doing one of these:
$zendDbTable->find(1,2) //is looking for a compound key
$zendDbTable->find(array(1,2)) //is looking for two rows
When you really want the opposite.