I am used to using db_select in drupal 7 but now it's deprecated in drupal 8
So, If I need to create a query to list all users from users_field_data table, What should I do?
Do I still use db_select or db_query even though they are deprecated functions? Or create a new controller to extend from "Select class" and make my query?
Depends on what you are trying to achieve.
Using the storage object
If you want to make a simple query about the users then you should use the loadByProperties of the storage object
$users = \Drupal::entityTypeManager()->getStorage('user')->loadByProperties([
'name' => 'bar'
]);
Using entity query & loadMultiple
If you need a more complex query with sorts, range, pager and OR/AND condition groups you should use entity query
$ids = \Drupal::entityQuery('user')->condition('name', 'foo')->execute();
$users = User::loadMultiple($ids);
As mentioned in the documentation you can query data by injecting Drupal's database connection class. For example:
use Drupal\Core\Database\Connection;
class DatabaseQueryExample {
protected $connection;
public function __construct(Connection $connection) {
$this->connection = $connection;
}
public function queryExamples() {
// db_query()
$this->connection->query(" ... ");
// db_select()
$this->connection->select(" ... ");
}
}
db_select, db_insert, db_update, etc... were deprecated in Drupal 8.0.x and will be removed in Drupal 9.0.0.
Instead get a database connection injected into your service from the container and call select() on it. For example, $injected_database->select($table, $alias, $options);.
eg:
$db = \Drupal::database();
$data = $db->select('table_name','t')->fields('t')->execute();
$db->insert();
$db->update();
Related
This question already has an answer here:
How to use PDO connection in other classes?
(1 answer)
Closed 2 years ago.
I have 2 class in my CMS and there are crud functions in "User Class". How can I use database class in another class. I know they both work. But I was previously transferring with the "extends" method. But every time I created an object, the database connection was duplicated. So "extends" was fault for me. I don't want to fault again.
So which is best solution? Do any of these solutions duplicate the database connection?
databaseclass.php:
class database
{
public $connect;
function __construct() {
$this->connect();
}
public function connect() {
$this->connect = new PDO('mysql:host=' . SERVER . ';dbname=' . DB_NAME . ';charset=utf8', DB_USER, DB_PASS);
}
}
userclass.php:
class user
{
function getUser() {
# select an user codes
}
}
Solution - Injection database class
class user
{
function getUser($database) {
$mysql_command = $database->connection->prepare('SELECT * FROM users WHERE id = 1');
$mysql_command->execute();
return $mysql_command->fetch(PDO::FETCH_ASSOC);
}
}
Solution - Global database class (I want that but everybody suggest first solution) (I want because this solution does not require me to inject each time I create a user object) userclass.php:
class user
{
function getUser() {
global $database;
$mysql_command = $database->connection->prepare('SELECT * FROM users WHERE id = 1');
$mysql_command->execute();
return $mysql_command->fetch(PDO::FETCH_ASSOC);
}
}
I added a third option here and gave some personal opinion about the others:
Solution a) Pass as parameter to your methods
This way you always need to pass it inside your functions which makes the API of your class less clean. Imagine we want to get a user by id. This could look like: getUserById($database, 123). This will require everywhere to know how to fetch the databse. I would try to avoid this.
Solution b) Use it as global
I would try avoid global wherever I can. My first argument would be that it makes your class less testable and can change the behaviour dependent on the context. Here is a nice post about it with more examples: https://stackoverflow.com/a/5166527/12880865
Solution c) Use dependency injection with the __constructor.
I would add another way of solving it by using dependency injection at constructor level already. This way you can use getUsers() or getUserById($id) without passing the database all the time. This way your class defines it's own dependencies very clearly for the user of this class.
this solution does not require me to inject each time I create a user object
To avoid this you could use container which could resolves your class dependencies automatically when you try to retrieve them.
Example with your code:
class user
{
protected $database;
public function __construct(database $database) {
$this->database = $database;
}
function all() {
# select an user codes
$this->database->query('....');
}
function getById($id) {
# select an user codes
$this->database->query('....', ['id' => $id]);
}
}
which you can then inject like this:
$database = new Database();
// Here you'll pass your database connection into the repository
$users = new UserRepository($database);
$users->all(); // returns an array of all users
$users->getById(123); // returns a specific user by id
// Here you'll pass the same database connection into another repository
$posts = new PostRepository($database);
$posts->all(); // returns an array of all posts
$posts->getById(123); // returns a specific post by id
I'm building my own CMS and I used my own Database class for it.
In some frameworks there is a way to do a function on another function
$firstValues = $this-db->query('your query')->first();
What do I kind of class do I need for the ->first().
Is there a way in php to do this? Do I need to use an Abstract class? Does anybody has an example?
If you need any more information, please ask me!
Thanks for helping!
Tom
What do I kind of class do I need for the ->first().
Most frameworks provide a "Query Builder" class to generate and execute SQL statements without writing SQL manually.
Is there a way in php to do this?
Yes, just pick a query builder:
https://github.com/cakephp/database
https://github.com/illuminate/database
Do I need to use an Abstract class?
No
Does anybody has an example?
Illuminate Database (Laravel):
$userRow = Capsule::table('users')->where('votes', '>', 100)->get();
CakePHP:
$userRow = $connection->newQuery()
->from('users')
->select('*')
->where(['votes >' => 20])
->execute()
->fetch('assoc');
Under the hood the first() method just fetches the first row.
PDO example:
$statement = $pdo->prepare("SELECT * FROM users WHERE votes > :votes LIMIT 1");
$statement->execute(['votes' => 20]);
$userRow = $statement->fetch();
You have to pass $this between methods.
Here's some example:
Class Db {
private $_query_result;
public function query($query_string = '')
{
// make your query or smth
// save for example array of results
$this->_query_result = $your_query_result;
return $this;
}
public function first()
{
return array_shift($this->_query_result);
}
}
I've made this class to handle all of my sql-queries. But I'm unsure of how to use it properly.
The class looks something like this (this is a VERY simple version of it):
class sql {
private $conn;
private $data;
function __construct() {
//makes connection to DB and sets $conn and $data
}
public function select($variables, $table, $criterias) {
//returns an array with all the info from DB
}
function __destruct() {
//closes the sql-connection
}
}
The question now is: Is this going to overload the DB, if I use it multiple times on every page-load? (refered to as Example #1)
$dbInfo = (new sql)->select($var,$tab,$cri);
$moreInfo = (new sql)->select($var2,$tab2,$cri2);
$evenMoreInfo = (new sql)->select($var3,$tab3,$cri3);
Would it be beneficial to make my sql class's methods static?
Or should I not create a new instance of a sql object every time I want to make a query (like the example below - refered to as Example #2)?
$sql = new sql();
$dbInfo = $sql->select($var,$tab,$cri);
$moreInfo = $sql->select($var2,$tab2,$cri2);
$evenMoreInfo = $sql->select($var3,$tab3,$cri3);
How and when is Example #1 the better choice over Example #2, and vice versa?
If I assume that Example #1 is going to take the most resources from the DB, when would you pick Example #1 over Example #2?
Your example 2 is more common to see, however the SQL object is usually a static/singleton. So it connects to the database once per server request.
Your base SQL object should handle connecting to a database and then handle basic input/output, such as executing a string of SQL and returning the results.
You can then add new objects on top of that for each object/table than then interfaces with this SQL singleton. These classes will handle constructing their custom SQL based on their table, joins, field names/types, etc.
E.g:
A very basic 'table' object looks like this
class SomeTableObject
{
m_TableName = 'SomeTable'; // Table to get Data from
function GetSelectSQL()
{
return "SELECT * FROM ".$this->m_TableName;
}
function Select($where)
{
$sql = $this->GetSelectSQL().$where;
return SqlSingleton::Execute($sql);
}
function GetByID($id)
{
$where = " WHERE FieldNameForID=$id";
return $this->Select($where);
}
}
These objects work better if they extend a base class that has those basic GetSelectSQL, TableName, Select, etc functions. The GetByIDs (and other gets, updates, inserts) will vary from table to table.
I use Redbeanphp ( http://redbeanphp.com/ ) in my php project. And i want to use a table prefix for my tables.
Redbeanphp can't support table prefix since the version 3.0. But i want to extend Redbeanphp to support table prefix in my project.
I don't want to modify the redbeanphp code. But if there's no solution, i'll do that.
I have already tried to replace the QueryWriter of Redbeanphp but the QueryWriter class is not always the same (it depends of the type of my database).
What is the best way to do that ?
I now got the response so i answer to myself.
Once redbean is initialized, you can configure a new toolbox. The toolbox in redbean handle 3 important objects : The query writer, the Redbean OODB and the database adapter. You can access the current redbean toolbox with R::$toolbox
You can do this code :
R::configureFacadeWithToolbox(new RedBean_ToolBox(R::$redbean, R::$adapter, R::$writer));
This code does nothing. Because you configure Redbean with a new toolbox but with the same OODB, the same database adapter and the same query writer. But in this code, you can replace one of these object by your own object.
Example, replacing the writer by a dummy writer :
$writer = new MyQueryWriter();
R::configureFacadeWithToolbox(new RedBean_ToolBox(R::$redbean, R::$adapter, $writer));
The probem is the following :
You want to replace the query writer by your own query writer to handle a table prefix
The query writer class is not always the same. Redbean use 5 classes for the query writer. The class depends of the database type. For instance, if you use a Mysql database, the query writer class is RedBean_QueryWriter_MySQL
You don't want to write an entire query writer.
Redbean query writer possible classes are :
RedBean_QueryWriter_CUBRID
RedBean_QueryWriter_MySQL
RedBean_QueryWriter_Oracle
RedBean_QueryWriter_PostgreSQL
RedBean_QueryWriter_SQLiteT
So, this is my solution. I wrote 5 littles classes.
class MyCubridQueryWriter extends RedBean_QueryWriter_CUBRID {
public function safeTable($name, $noQuotes = false) {
$name = prefix($name);
return parent::safeTable($name, $noQuotes);
}
}
class MyMysqlQueryWriter extends RedBean_QueryWriter_MySQL {
public function safeTable($name, $noQuotes = false) {
$name = prefix($name)
return parent::safeTable($name, $noQuotes);
}
}
class MyOracleQueryWriter extends RedBean_QueryWriter_Oracle {
public function safeTable($name, $noQuotes = false) {
$name = prefix($name)
return parent::safeTable($name, $noQuotes);
}
}
class MyPostgreSqlQueryWriter extends RedBean_QueryWriter_PostgreSQL {
public function safeTable($name, $noQuotes = false) {
$name = prefix($name)
return parent::safeTable($name, $noQuotes);
}
}
class MySQLiteTQueryWriter extends RedBean_QueryWriter_SQLiteT {
public function safeTable($name, $noQuotes = false) {
$name = prefix($name)
return parent::safeTable($name, $noQuotes);
}
}
As you can see, each class extend a Redbean query writer class. We override the safeTable method. Redbean always use safeTable on a table name. The prefix function is simple :
function prefix($table) {
return "my_prefix_$table";
}
So now, in our code. We can use an array to map a Redbean query writer class to our own classes and replace it. Here we are :
$writerMapping = array(
'RedBean_QueryWriter_CUBRID' => 'MyCubridQueryWriter',
'RedBean_QueryWriter_MySQL' => 'MyMysqlQueryWriter',
'RedBean_QueryWriter_Oracle' => 'MyOracleQueryWriter',
'RedBean_QueryWriter_PostgreSQL' => 'MyPostgreSqlQueryWriter',
'RedBean_QueryWriter_SQLiteT' => 'MySQLiteTQueryWriter'
);
$class = $writerMapping[get_class(R::$writer)];
$writer = new $class(R::$adapter);
R::configureFacadeWithToolbox(new RedBean_ToolBox(R::$redbean, R::$adapter, $writer));
Et voila. Now Redbean will use your own writer and you can do what you want ! With our safeTable method, we add a prefix to every table name in the database.
I ran into this problem when wanting to use RedBean with Wordpress. My solution was to create another class (WPR for "wordpress redbean"), like so:
class WPR {
public static function __callStatic($method, $params)
{
global $wpdb;
$prefix = $wpdb->base_prefix;
foreach ($params as &$param)
$param = preg_replace('/\{([a-zA-Z0-9_]+)\}/', $prefix . '$1', $param);
// switch to wordpress database
R::selectDatabase('WPR');
// do the dang thing
$res = call_user_func_array(array('R',$method),$params);
// go back
R::selectDatabase('default');
// send it
return $res;
}
};
R::addDatabase('WPR', "mysql:host=".DB_HOST.";dbname=".DB_NAME, DB_USER, DB_PASSWORD);
I also wanted this class to use a different database than my 'regular' redbean class, so I have the selectDatabase() calls in there. Comment them out if you don't need them.
What it does is it acts as a proxy to redbean, but with each input it checks for some substring like {this} and it expands it out into the full database name, with prefix. Here's an example of your usage:
$my_blog = WPR::find('{blogs}', 'domain=?', array('mydomain.com')); or
$allowed_hosts = WPR::getCol('SELECT domain FROM {blogs}');
In those two cases, {blogs} gets converted to wp_blogs
Magus,
I have the same problem as you. I tried your solution but could not get it working. I wrote a couple of functions for prefixing and my object names into table names and back, which I think will work in my case, but I'd still like to get your way working since it'll be more transparent. I have unprefixed table names working for reading and writing.
I noticed was that Oracle support isn't available out-of-the-box in RedBean, so I added checks for each classname to avoid errors:
if (class_exists('RedBean_QueryWriter_MySQL', false)) {
class MyMysqlQueryWriter extends RedBean_QueryWriter_MySQL {
...
}
The checks should work, I got output to my log within my MySQL (which I'm using) block while loading the prefixing code.
Also, at the end there you wrote:
$class = $writerMapping(get_class(R::$writer));
but you probably meant:
$class = $writerMapping[get_class(R::$writer)];
Based on some debugging, my R::$writer has been changed after configureFacadeWithToolbox, but, for some reason the table names aren't being converted, and nothing within my custom safeTable function is being executed.
If you could give any more info on how you tested your method or what I could be missing, I'd be glad to hear it.
(I'm sorry this message isn't an answer to your question, but I really couldn't find any other way to send you a message or comment on your answer. Damn Stack Overflow! (Just kidding, I love it.))
i'm new to zend framework, in this simple function i want to get a single 'post' and then i want to find all the comments in the related table
public function getPost($idPost)
{
$db= Zend_Registry::get('db');
$select=$db->select()
->from($this->_name, '*')
->where("idPost= ".$db->quote($idPost, 'INTEGER'));
$stmt=$select->query();
$rowset=$stmt->fetchAll();
$post=$rowset->current();
//ora devo aggiungerci i commenti che questo post ha ricevuto
$comm=$post->findDependentRowset('commenti');
$ris=array($post, $comm);
return $ris;
}
in my index controller i i simply call this function, but i get this error:
Call to a member function current() on a non-object in C:\xampp\htdocs\...
where's the mistake?
I think you have a few misconceptions about how you're using Zend_Db.
1. You're not using the ORM, just the PDO wrapper
Which means, your queries won't return Zend rowsets and rows and therefore you can't use the methods of you can use on those.
2. The default fetch mode
The default fetch mode of the Zend_Db_Statement fetchAll() method is array, if you want it to return an object (stdClass), change the fetch mode before fetching the data:
$stmt->setFetchMode(Zend_Db::FETCH_OBJ);
3. Using fetchAll() when you actually want one row
If you just want one row, then don't fetch a whole table! With Zend_Db_Statement, use for example:
$row = $stmt->fetch();
or
$rowObj = $stmt->fetchObject();
... again, that's not a zend row object, just a stdClass instance, but you can do:
$rowObj->some_field;
on it.
On the other hand, if this is a method in your Post model, it should look something like:
public function getPost($idPost)
{
return $this->getRow($idPost);
}
This will return the post, then, if you've setup the table relationships correctly, you can also query for the dependent data or just get all comments with that id separately.
The problem is that unless you define a table class as was previously mentioned you can't uuse the dependent or parent rowsets.
To make your current function work would be best done with two functions, and keep it simple:
public function getPost($idPost)
{
$db= new Zend_Db_Table($this->_name);
$select=$db->select()
->where("idPost= ?", $idPost);
/*Fetch just the row you want, or use fetchAll() if you need to match return types*/
$row = $db->fetchRow($select);
return $row;
}
public function getComments($table='comments', $id) {
$db = new Zend_Db_table($table);
$select = $db->select()->where('post_id = ?', $id)->order('date ASC');
$rowset = $db->fetchAll($select);
return $rowset/* or you could return an array ->$rowset->toArray() */
}
Zend_Db_Table is going to attempt to use the current database adapter, so all you need to do is pass in the tablename.
One more note: you don't need to use any of the quote() function when using select() it's taken care of.
But it is really important, that if you are going to use Zend_Db, you need to learn about "Defining table classes". At least enough to use them in your own classes.
I hope this helps!
To get a rowset and dependent rowset you have to use Zend_Db_Table.
You only use the Zend_Db_Adapter with Zend_Db_Select.
Read from here.
So you have to define a class which extends from Zend_Db_Table_Abstract.
Example:
class Bugs extends Zend_Db_Table_Abstract
{
protected $_name = 'bugs';
protected $_primary = 'bug_id';
}
To get the Zend_Db_Table_Rowset object use:
$bugs = new Bugs();
$rowset = $bugs->fetchAll("bug_status = 'NEW'");
To find dependent rowsets you have to define the relation in your table class. Look here how to define relationships.