Is there a quick way to modify a SQL query generated by Laravel's Fluent to have an INSERT IGNORE instead of the usual INSERT?
I'm trying to insert an array with fifty elements. Writing out the entire query manually will bloat up the code and make it more susceptible to human errors.
Try this magic, in your model:
public static function insertIgnore($array){
$a = new static();
if($a->timestamps){
$now = \Carbon\Carbon::now();
$array['created_at'] = $now;
$array['updated_at'] = $now;
}
DB::insert('INSERT IGNORE INTO '.$a->table.' ('.implode(',',array_keys($array)).
') values (?'.str_repeat(',?',count($array) - 1).')',array_values($array));
}
Use like this:
Shop::insertIgnore(array('name' => 'myshop'));
This is a great way to prevent constraint violations that may occur with firstOrCreate in a multi-user environment, if that 'name' property was a unique key.
I couldn't monkey patch as suggested in Rastislav's answer.
This is what worked for me:
Override compileInsert method in a custom Query Grammar class, which extends the framework's MySqlGrammar class.
Use an instance of this custom grammar class by calling the setQueryGrammar method from the DB connection instance.
So, the class code is like this:
<?php
namespace My\Namespace;
use Illuminate\Database\Query\Builder;
use Illuminate\Database\Query\Grammars\MySqlGrammar;
/**
* Changes "INSERT" to "INSERT IGNORE"
*/
class CustomMySqlGrammar extends MySqlGrammar
{
/**
* Compile an insert statement into SQL.
*
* #param \Illuminate\Database\Query\Builder $query
* #param array $values
* #return string
*/
public function compileInsert(Builder $query, array $values)
{
// Essentially we will force every insert to be treated as a batch insert which
// simply makes creating the SQL easier for us since we can utilize the same
// basic routine regardless of an amount of records given to us to insert.
$table = $this->wrapTable($query->from);
if (! is_array(reset($values))) {
$values = [$values];
}
$columns = $this->columnize(array_keys(reset($values)));
// We need to build a list of parameter place-holders of values that are bound
// to the query. Each insert should have the exact same amount of parameter
// bindings so we will loop through the record and parameterize them all.
$parameters = collect($values)->map(function ($record) {
return '('.$this->parameterize($record).')';
})->implode(', ');
return "insert ignore into $table ($columns) values $parameters";
}
}
I copied the compileInsert method from the framework's class and then, inside the method, I have only changed insert to insert ignore. Everything else has been kept the same.
Then, in the specific spot of code, in the application (a scheduled task), where I needed "insert ignore", I have simply done as follows:
<?php
use DB;
use My\Namespace\CustomMySqlGrammar;
class SomeClass
{
public function someMethod()
{
// Changes "INSERT" to "INSERT IGNORE"
DB::connection()->setQueryGrammar(new CustomMySqlGrammar());
// et cetera... for example:
ModelClass::insert($data);
}
}
Updated answer for Laravel Eloquent in 2018
This also handles multiple simultaneous inserts (instead of one record at a time).
Warning: Eric's comment below is probably correct. This code worked for my past project, but before using this code again, I'd take a closer look at it and add test cases and adjust the function until it always works as intended. It might be as simple as moving the TODO line down outside the if braces.
Either put this in your model's class or in a BaseModel class that your model extends:
/**
* #see https://stackoverflow.com/a/25472319/470749
*
* #param array $arrayOfArrays
* #return bool
*/
public static function insertIgnore($arrayOfArrays) {
$static = new static();
$table = with(new static)->getTable(); //https://github.com/laravel/framework/issues/1436#issuecomment-28985630
$questionMarks = '';
$values = [];
foreach ($arrayOfArrays as $k => $array) {
if ($static->timestamps) {
$now = \Carbon\Carbon::now();
$arrayOfArrays[$k]['created_at'] = $now;
$arrayOfArrays[$k]['updated_at'] = $now;
if ($k > 0) {
$questionMarks .= ',';
}
$questionMarks .= '(?' . str_repeat(',?', count($array) - 1) . ')';
$values = array_merge($values, array_values($array));//TODO
}
}
$query = 'INSERT IGNORE INTO ' . $table . ' (' . implode(',', array_keys($array)) . ') VALUES ' . $questionMarks;
return DB::insert($query, $values);
}
Use like this:
Shop::insertIgnore([['name' => 'myShop'], ['name' => 'otherShop']]);
Answer for Laravel 5.8.33+
If anyone reads this nowadays: there's no need for any hacks or Query Builder extensions. The query builder natively provides an insertOrIgnore method that does just that.
Just use
DB::table('tablename')->insertOrIgnore([
['column_name' => 'row1', 'column2_name' => 'row1'],
['column_name' => 'row2', 'column2_name' => 'row2']
]);
See the documentation or the API docs for details.
Add the follow method insertIgnore to your Model
<?php
namespace App;
use Illuminate\Auth\Authenticatable;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Auth\Passwords\CanResetPassword;
use Illuminate\Foundation\Auth\Access\Authorizable;
use Illuminate\Contracts\Auth\Authenticatable as AuthenticatableContract;
use Illuminate\Contracts\Auth\Access\Authorizable as AuthorizableContract;
use Illuminate\Contracts\Auth\CanResetPassword as CanResetPasswordContract;
class User extends Model implements AuthenticatableContract,
AuthorizableContract,
CanResetPasswordContract
{
use Authenticatable, Authorizable, CanResetPassword;
/**
* The database table used by the model.
*
* #var string
*/
protected $table = 'users';
/**
* The attributes that are mass assignable.
*
* #var array
*/
protected $fillable = ['name', 'email', 'password'];
/**
* The attributes excluded from the model's JSON form.
*
* #var array
*/
protected $hidden = ['password', 'remember_token'];
public static function insertIgnore(array $attributes = [])
{
$model = new static($attributes);
if ($model->usesTimestamps()) {
$model->updateTimestamps();
}
$attributes = $model->getAttributes();
$query = $model->newBaseQueryBuilder();
$processor = $query->getProcessor();
$grammar = $query->getGrammar();
$table = $grammar->wrapTable($model->getTable());
$keyName = $model->getKeyName();
$columns = $grammar->columnize(array_keys($attributes));
$values = $grammar->parameterize($attributes);
$sql = "insert ignore into {$table} ({$columns}) values ({$values})";
$id = $processor->processInsertGetId($query, $sql, array_values($attributes));
$model->setAttribute($keyName, $id);
return $model;
}
}
You can use:
App\User::insertIgnore([
'name' => 'Marco Pedraza',
'email' => 'mpdrza#gmail.com'
]);
The next query it will be executed:
insert ignore into `users` (`name`, `email`, `updated_at`, `created_at`) values (?, ?, ?, ?)
This method automatically add/remove the Eloquent timestamps if you have enabled or disabled.
For the job you need to create a new Grammar that will have the right string in there:
grammar.php (1)
The grammar is a public property of the DB or in this case Database stored connection. This is not really straight forward, but from the visibility of the properties you should be able to inject your special grammar into the database layer.
I also suggest you bring the issue up with the project, they probably have got a better idea how to make that more flexible for cases like these.
(1) This was a former, to the date of the answer reference. If you see this today, you need to adopt to the Laravel version you use, e.g. Grammar.php for 4.0, these classes have moved into laravel/framework.
Not sure if helpful for anybody but recently I have adapted hakre's approach to Laravel 5:
You have to change following 3 files to have your Insert Ignore working:
In Builder.php (vendor/laravel/framework/src/illuminate/database/query/Builder.php) you have to clon the function insert, with the change in name to insertIgnore and change in the grammar call function to: $sql = $this->grammar->compileInsertIgnore($this, $values);)
In Grammar.php (vendor/laravel/framework/src/illuminate/database/query/grammars/Grammar.php) you have to clone the compileInsert function and rename it to compileInsertIgnore, where you change return to: return "insert ignore into $table ($columns) values $parameters";
In Connection.php (vendor/laravel/framework/src/illuminate/database/Connection.php) you have to simply clone the function insert and rename it to insertIgnore
Now you should be done, connection is able to recognise the function insertIgnore, builder is able to point it to correct grammar and grammar includes 'ignore' in the statement. Please note this works well for MySQL, might not be this smooth for other databases.
I lastly found this one https://github.com/yadakhov/insert-on-duplicate-key which helped me alot
User::insertIgnore($users);
this is the method I am using, giving array of rows to it and its returning effected rows
install it through composer: composer require yadakhov/insert-on-duplicate-key
Option to avoid writing code is:
https://github.com/guidocella/eloquent-insert-on-duplicate-key
I have tested it just now - it works with my 5000 inserts at a time sometimes with duplicates...
With it you will get these functions:
User::insertOnDuplicateKey($data);
User::insertIgnore($data);
$your_array = array('column' => 'value', 'second_column' => 'value');
DB::table('your_table')->insert($your_array);
Keep in mind, I don't know where your data is coming from, but you should sanitize it, always. If you have more than one record, just iterate over in a loop.
As far as the INSERT IGNORE, find the INSERT method in the fluent library, make a new method called insert_ignore the exact same way as insert and just modify with the IGNORE.
Related
I would like to convert a timestamp and have some other values related to it. My question is how I can introduce my own method like DB::raw() that appends everything to the current select values.
So, for instance, for something like this
$user = DB::table('users')
->select('*', DB::timestamp('timestamp_column', 'convert_timezone', 'called_as'))
->where('id', 1)->first();
Let's assume that I am trying to get the value for created_at column and it's called as converted_created_at and it should return something like below.
{
id: 1,
name:'John Doe',
converted_created_at: {
'utc_time': 'created_at value as that is in utc by default',
'converted_time': 'timestamp converted into user timezone',
'diff': '10 hours ago' // difference between created_at and current timestamp
}
}
So, how do I introduce my own method that does this?
You can take example of any SQL database as you wish.
I know I can do that with Model but I wanted to see how to approach this problem using a facade.
Thank you in advance for your help.
First look here: https://stackoverflow.com/a/40615078/860099 - Try this Extend DB facade:
namespace App\Facades;
use Illuminate\Support\Facades\DB as DBBase;
class DB extends DBBase {...}
and in config/app.php change
'DB' => Illuminate\Support\Facades\DB::class,
to
'DB' => App\Facades\DB::class,`
(i write code from head)
Alternative:
You can easily create helper class eg. DBTools witch static methods and inside that methods you will use DB and construct proper query. And use it like that DBTools::yourMethod(...)
As argument to that method you can give... QUERY here is example of calling this method
DBTools::yourMethod(User::query())->first();
and inside you can easyily manipulate that query and return updated version.
ALTERNATIVE: If your goal is to add some new filed in Model (json) that not exist in db but is generated then you can use $appends (look: mutators and appends)
class User extends Model
{
protected $appends = ['converted_created_at'];
...
public function getConvertedCreatedAtAttribute() {
return ...; // return generated value from other fields/sources
}
Thanks to #kamil for showing me the way.
I am writing an answer in case anyone in the future finds this helpful.
I have come up with my own method that helps to convert timezone easily without writing too much code inside select query for DB facade for PostgreSQL.
I have created a file like this now.
<?php
namespace App\Custom\Facade;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\DB;
class DBTools extends DB
{
/**
* Convert a timestamp
* #param $timestamp - timestamp to be converted
* #param bool $insideRaw - if this helper method is getting used inside DB::raw() method
* #param null $timezone
* #param null $format - time format
* #param null $calledAs - column to called as
* #return \Illuminate\Database\Query\Expression|string
*/
public static function convertTime($timestamp, $insideRaw = false, $timezone = null, $format = null, $calledAs = null)
{
if (Auth::check()) {
if (!$timezone)
$timezone = Auth::user()->timezone;
if (!$format)
$format = Auth::user()->time_format;
}
$query = "to_char($timestamp at time zone '$timezone', '$format')" . ($calledAs ? " as $calledAs" : '');
if (!$insideRaw) {
return DB::raw($query);
}
return $query;
}
}
Now this can be easily be called inside select for DB facade or inside DB::raw() in case you're handling much more complicated query.
Hope this helps someone.
How does one go about escaping parameters passed to a raw query in Laravel 4? I expected something like DB::escape() (which rings a bell from Laravel 3) and also attempted DB::quote() (which I thought could be available through the PDO object)
$query = DB::select("SELECT * FROM users WHERE users.id = " . DB::escape($userId));
We can't use the select method with placeholders as the above is just a simplified example of what we are trying to achieve. We have a large custom query with a few nested select queries that cannot be adapted to the query builder.
What is the best approach to escaping something prior to inserting in Laravel 4?
EDIT:
I've just discovered that you can access the PDO object and use the quote function on it this way. Is this still the best approach, or is there an easier way to access this function?
DB::connection()->getPdo()->quote("string to quote");
You can quote your strings this way, through the DB facade.
DB::connection()->getPdo()->quote("string to quote");
I did put this answer in my question when I discovered it, however I've now put it in as an actual answer to make it easier for others to find.
$value = Input::get("userID");
$results = DB::select( DB::raw("SELECT * FROM users WHERE users.id = :value"), array(
'value' => $value,
));
More Details HERE
You may also try this, (Read Documentation)
$results = DB::select('SELECT * FROM users WHERE users.id = ?', array($userId));
Two answers here, that I use, have less verbose solutions built into the DB facade.
First, value quoting:
// From linked answer
DB::connection()->getPdo()->quote("string to quote");
// In the DB facade
DB::getPdo()->quote('string to quote');
Second, identifier quoting (table and column names):
// From linked answer
DB::table('x')->getGrammar()->wrap('table.column');
// In the DB facade
DB::getQueryGrammar()->wrap('table.column');
I found this question when looking for generic sql escaping in Laravel. What I actually needed though was table/column name escaping. So, for future reference:
/**
* Quotes database identifier, e.g. table name or column name.
* For instance:
* tablename -> `tablename`
* #param string $field
* #return string
*/
function db_quote_identifier($field) {
static $grammar = false;
if (!$grammar) {
$grammar = DB::table('x')->getGrammar(); // The table name doesn't matter.
}
return $grammar->wrap($field);
}
I'm using this in my helpers.php at Laravel 5:
if ( ! function_exists('esc_sql'))
{
function esc_sql($string)
{
return app('db')->getPdo()->quote($string);
}
}
Then I can use esc_sql function where I need to pergorm escaping for raw SQL queries.
Here's a fuller example, showing how to escape both values and columns and extend Laravel's querybuilder:
<?php
namespace App\Providers;
use Illuminate\Database\Query\Builder;
use Illuminate\Support\ServiceProvider;
class DatabaseQueryBuilderMacroProvider extends ServiceProvider {
public function register() {
Builder::macro('whereInSet', function($columnName, $value) {
/** #var \Illuminate\Database\Query\Grammars\Grammar $grammar */
$grammar = $this->getGrammar();
return $this->whereRaw('FIND_IN_SET(?,' . $grammar->wrap($columnName) . ')', [$value]);
});
}
}
PHP Heredoc
<?php
use Illuminate\Support\Facades\DB;
$sql = <<<SQL
WITH table1 AS(SELECT...
SQL;
$parameters = [1,2,3,...]
$table = DB::select($sql, $parameters);
I've designed my database in MySQL Workbench, and have all my foreign keys setup, etc.
I'm wanting to use this DB schema with Laravel 4, however from the docs there is no word of any sort of ability to work with an existing set of database tables. From my understanding, other frameworks such as Cake with its 'Baking' allow you to automatically generate your model classes based on the tables already in your database.
I've looked around everywhere and cant see anything about this at all for Laravel 4. The closest thing I've found is Jeffrey Way's Generator package for artisan, however this only creates the base model, and doesn't detect established foreign key relationships.
Is this even possible with Laravel 4 or am I going to have to just do it all manually?
The good news is that Antonio just finished his MySQL WorkBench to Eloquent ORM converter
This is a beautiful solution but comes a way to late for me but may help you a lot.
Update: The link isn't working in the moment. The wabpage says "We are redesigning things, will be back soon!". I allready sent antonio an email asking him, when this service will be available again.
Antonio said that it'll be back but there is no estimated time of arrival. We have to wait..
cakePHP does a great job at fleshing out your whole project from the DB schema already in place. Laravel currently does not support anything like this. One of the minor features still holding me back from adopting laravel.
Hmm I had the same issue and I wrote a little script myself which generates base classes and solves the foreign key issues. It's a basic solution and only determines "hasOne" relations, which you might have to change to hasMany later on. I used a Controller and build my code Template in a view:
Controller:
namespace Admin;
/**
* just a quick helper to generate model classes
* from mysql to Eloquent style ones..
* #author Mario
*/
class ModelController extends \BaseController {
/**
* save Classes in folder of choice
*
* #return void
*/
public function create($folder)
{
$sql = "SELECT * FROM information_schema.tables WHERE table_schema = 'UR_SCHEMA'";
$tables = \DB::select($sql);
$sql2 = "select * from information_schema.`KEY_COLUMN_USAGE` where constraint_schema = 'UR_SCHEMA' order by table_name";
$keys = \DB::select($sql2);
$meta = $this->sortOutMetadata($keys);
foreach ($tables as $table) {
$metaData = null;
if(!empty($meta[$table->TABLE_NAME])){
$metaData = $meta[$table->TABLE_NAME];
}
$code = \View::make('model.start', array('table' => $table, 'meta' => $metaData))->render();
file_put_contents($folder.DIRECTORY_SEPARATOR.ucfirst(camel_case($table->TABLE_NAME).'.php'), $code);
}
}
/**
* provide structure indexed by table
*
* #param type $keys
* #return type
*/
private function sortOutMetadata($keys)
{
$return = array();
foreach ($keys as $key) {
if ($key->CONSTRAINT_NAME == 'PRIMARY') {
$return[$key->TABLE_NAME]['pk'] = $key->COLUMN_NAME;
} elseif (!empty($key->REFERENCED_TABLE_NAME)) {
//one way
$return[$key->TABLE_NAME]['fk'][] = array('column' => $key->COLUMN_NAME,
'refColumn' => $key->REFERENCED_COLUMN_NAME,
'refTable' => $key->REFERENCED_TABLE_NAME,);
//and the other
$return[$key->REFERENCED_TABLE_NAME]['fk'][] = array('column' => $key->REFERENCED_COLUMN_NAME,
'refColumn' => $key->COLUMN_NAME,
'refTable' => $key->TABLE_NAME,);
}
}
return $return;
}
}
My view Template (pretty much my Class Template)
<?php echo '<?php'; ?>
namespace Model\Base;
use Model\Model;
class <?php echo ucfirst(camel_case($table->TABLE_NAME));?> extends Model {
/**
* #var String
*/
protected $table = '<?php echo $table->TABLE_NAME;?>';
<?php if (isset($meta['pk'])):?>
/**
* #var String
*/
protected $primaryKey = '<?php echo $meta['pk'];?>';
/**
* attributes not writable from outside
* #var mixed
*/
protected $guarded = array('<?php echo $meta['pk'];?>');
<?php endif;?>
/**
* Timestamps we dont want here
* #var Boolean
*/
public $timestamps = false;
<?php if (isset($meta['fk'])):?>
<?php foreach($meta['fk'] as $keys):?>
/**
* #return HasOne
*/
public function <?php echo camel_case($keys['refTable']);?>()
{
return $this->hasOne('Model\<?php echo ucfirst(camel_case($keys['refTable']));?>', '<?php echo $keys['refColumn'];?>', '<?php echo $keys['column'];?>');
}
<?php endforeach;?>
<?php endif;?>
}
Then Simply generate your base classes by giving it the folder: (from wherever you prefer)
$controller = new \Admin\ModelController();
$controller->create(__DIR__ . DIRECTORY_SEPARATOR . 'tmpModel');
This gave me some decent way to get my base classes Auto generated the way I needed. Remember you got to be able to see the information_schema schema with your db user.
Hope this helps
While working on a mapping structure for our applications we ran into some trouble regarding the code consistency. While it's easy to make select query's with the Zend_Db_Select class (with functions like: $select->from('table')->where('id=?,1), it wouldn't work for update/delete query's. There isn't a class like Zend_Db_Update or Zend_Db_Delete to build the update and delete query's like you build the select. To fix this we extended the Zend_Db_Select class as you can see in the code below. The code shows the custom class that extends the Zend_Db_Select class with some minimal example code at the bottom to show how it's used.
<?php
class Unica_Model_Statement extends Zend_Db_Select
{
public function __construct($oMapper)
{
parent::__construct($oMapper->getAdapter());
$this->from($oMapper->getTableName());
}
/**
* #desc Make a string that can be used for updates and delete
* From the string "SELECT `column` FROM `tabelnaam` WHERE `id` = 1" only the part "`id = `" is returned.
* #return string
*/
public function toAltString()
{
$sQuery = $this->assemble(); // Get the full query string
$sFrom = $this->_renderFrom(''); // Get the FROM part of the string
// Get the text after the FROM (and therefore not using the "SELECT `colname`" part)
$sString = strstr($sQuery,$sFrom);
// Delete the FROM itself from the query (therefore deleting the "FROM `tabelnaam`" part)
$sString = str_replace($sFrom, '', $sString);
// Delete the word "WHERE" from the string.
$sString = str_replace('WHERE', '', $sString);
return $sString;
}
}
################################################
# Below code is just to demonstrate the usage. #
################################################
class Default_IndexController extends Zend_Controller_Action
{
public function indexAction()
{
$person = new Unica_Model_Person_Entity();
$statement = new Unica_Model_Statement($person->getMapper());
$statement->where('id = ?' , 1);
$person->getMapper()->delete($statement);
}
}
class Unica_Model_Person_Mapper
{
public function delete($statement)
{
$where = $statement->toAltString();
$this->getAdapter()->delete($this->_tableName,$where);
}
}
Everything works fine using this class but it got us wondering if we were maybe missing something. Is there a reason there aren't default update/delete classes like there is for select and will using this class give us trouble elsewhere?
Advice would be appreciated.
Thanks in advance,
Ilians
The class is fine if you are sure you are not going to make it evolve too much in the future. I assume your approach is to benefit from the automatic quoting in the Zend_Db_Select class. In my humble opinion, however, it has some design pitfalls that could lead to extensibility troubles if you need to modify or extend it:
It's receiving some data that is discarded afterwards (the entity object, to use the "from" clause).
It's manipulating directly the SQL output of the select query, which can be something dangerous to rely upon. If the format changes, and also if you need to include more elements to the where clause, your code could get quite "muddy" to adapt to the changes.
My approach would just be to use the where clauses directly in the code, and quote them wherever it's necessary. It doesn't look particularly less clean to me. Something like the following:
$db->delete('tablename', 'id = ' . $db->quote('1'));
Eventually, you could even abstract it into a method or class, to avoid having to spread $db->quote() calls all over the place, something like that:
private function myDelete($tableName, $fieldName, $fieldValue)
{
$this->db->delete($tableName, $fieldName . ' = ' . $db->quote($fieldValue));
}
EDIT: Including multiple clauses in the where part would make it a bit more involved, and how flexible you want to be will depend on your particular application. For instance, a possible solution for several "ANDed" clauses could be the following:
private function myDelete($tableName, array $fields)
{
$whereClauses = array();
foreach ($fields as $fieldName => $fieldValue) {
$whereClauses[] = $fieldName . ' = ' . $db->quote($fieldValue);
}
$this->db->delete($tableName, implode(' AND ', $whereClauses));
}
$this->myDelete('tablename', array('id' => '1', 'name' => 'john'));
Hope that helps,
I dont know the exact reasoning behind Zend_Db_Select not offering CUD methods. The obvious explanation is that Select means you should use it for just that: dynamic query building. To insert, update and delete you would either use Zend_Db_Adapter or the proxy methods to it in Zend_Db_Table and Zend_Db_Table_Row or the generic Zend_Db_Statement.
However, with that said, I think there is nothing wrong with extending Zend_Db_Select and adjusting it your needs (just like any other component that doesnt do what you want initially)
I writing real estate web site
with basic function for choosing and ordering realty.
It is small/simple project, but I want to write it in way,
so in future I, or other developers, can turn it into medium business app without rewriting it
from scratch.
So what kind of patterns could you advice me to use for dealing with database?
For now I have this:
class db_DBConnection
{
// basic singleton pattern here...
}
// primary class to be extende by other table DAOs
abstract class db_Table
{
protected $table;
protected $order_by;
/**
* Executes specified query with prepared statements
* returns statement object, which can fetch data.
*
* #param $sql - SQL query to execute
* #param $params - bind values to markers through associative arrays
*/
protected function executeQuery($sql, $params = null)
{
$dbh = db_DBConnection::getConnection();
$stmt = $dbh->prepare($sql);
// binds values to markers and executes query
$stmt->execute($params);
return $stmt;
}
/**
* #param id - id of row to retrieve from database
*
* It sends SQL query and id to executeQuery
* function returns associative array, representing
* database row.
*/
public function find($id)
{
$sql = 'SELECT * FROM ' . $this->table . ' WHERE id=:id LIMIT 1';
// bind id
$params = array( ':id' => $id );
// execute and return associative array
return $this->executeQuery($sql, $params)->fetch(PDO::FETCH_ASSOC);
}
public function findAll($quantity, $where)
{
// Returns array of
// associative arrays of table rows :)
// TODO: write this function
}
abstract protected function insert();
abstract protected function update();
abstract protected function delete();
// ...
The best way would be to use an ORM, like Doctrine. It might seem a little too much for smaller type project, but it pays off in a long run.
It is better to use standard ways of doing things, instead of reinventing your own.
Here is a list of ORMS from Wikipedia.
Also you need to evaluate your project, creating project freestyle might not be a very good idea. Other developers will have to learn your code and understand how it works, etc... It is better to use well know frameworks like Zend Framework, Symfony or CakePHP. You can also look into expandable CMS systems like Joomla and Drupal.