JOIN works in SQL not in Drupal - mysql

I'm trying to make a module for Drupal 7.x. At a certain point I want to use a sql query (JOIN). When I try the query in MYSQL it works. But when I want to try it in Drupal, the array is empty.
So I guess there is a difference between the sql query and the drupal query (mayby the implemantion is different).
SQL Query
SELECT * FROM friends
INNER JOIN users
ON friends.uid=users.uid
Drupal implementation
function project_myfriends(){
// Use database API to retrieve tasks
$query = db_select('friends', 'f');
$query->join('users', 'u', 'f.uid = u.uid'); // JOIN
$query->fields('u', array('name'))
->execute();
return $query;
}
/**
* Implements hook_block_view().
*/
function project_block_view($delta = ''){
switch ($delta) {
case 'project':
$block['subject'] = t('My Friends');
// Use our custom function to retrieve data
$result = project_myfriends();
$items = array();
var_dump($result);
foreach($result as $friend){
$items[] = array(
'data' => $friend->name,
);
}
// No tasks
if (empty($items)) {
$block['content'] = t('No friends.');
}
else {
// Pass data trough theme function
$block['content'] = theme('item_list', array(
'items' => $items));
}
}
return $block;
}
Thx in advance

You forgot to fetch your result query:
$result = project_myfriends()->execute()->fetchAll();
var_dump($result);

Related

laravel - ErrorException Array to string conversion

I'm trying to figure out how I can get the query result like $residence into the data array. because whem im doing this is gives me the error Array to string conversion. Is there any possible way to convert the query result to a normal string?
/**
* Store a newly created resource in storage.
*
* #param \Illuminate\Http\Request $request
* #return \Illuminate\Http\Response
*/
public function insert(Request $request)
{
$id = auth()->user()->id;
$title = $request->input('title');
$clientid = $request->input('client');
$startdate = $request->input('startdate');
$enddate = $request->input('enddate');
$starttime = $request->input('starttime');
$endtime = $request->input('endtime');
$description = $request->input('description');
$firstname = DB::select('select firstname from clients where id='.$clientid);
$lastname = DB::select('select lastname from clients where id='.$clientid);
$housing = DB::select('select housing from clients where id='.$clientid);
$housenr = DB::select('select housenr from clients where id='.$clientid);
$residence = DB::select('select residence from clients where id='.$clientid);
$residencestring = json_encode($residence);
$data=array(
"uuid"=>$id,
"title"=>$title,
"residence"=>$residencestring,
"startdate"=>$startdate,
"enddate"=>$enddate,
"starttime"=>$starttime,
"endtime"=>$endtime,
"description"=>$description,
"firstname"=>$firstname,
"lastname"=>$lastname,
"housing"=>$housing,
"housenr"=>$housenr
);
//dd($data);
DB::table('tasks')->insert($data);
return redirect('/todo');
}
Notice how you are doing one query for each field? Also, you are getting an array on each query, since the DB::select returns an array with one row, not the row directly as you think.
I would use Query Builder for this for a more elegant solution:
$client = DB::table('clients')->where('id', $clientid)->first();
With this, you have an object named $client that has all the fields from that row.
Then, you can just update the row as follows:
$data = [
'lastname' => $client->lastname,
'firstname' => $client->firstname
];
You could even make it more "Laravel" by using Models.
App/Models/Client.php
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Client extends Model {
protected $guarded = ['id'];
}
Then your code would look something like this:
<?php
use App\Models\Client;
public function insert(Request $request)
{
....
$client = Client::findOrFail($clientid);
$data = [
'lastname' => $client->lastname,
'firstname' => $client->firstname
];
....
}
The findOrFail function gives you the first register it finds on the table based, equivalent to a "where id=$clientid"
You could go even further and insert using Eloquent as well, as so:
$task = new Task;
$task->lastname = $client->lastname;
$task->firstname = $client->firstname;
$task->save();
or:
$task = Task::insert($data);
where Task is a Model as described previously.
So big thanks to #JoeGalind1! The solution was pretty simple, I had to use the build-in query builder. Instead of using an oldschool query.
This is the solution that worked for me!
$client = DB::table('clients')->select('*')->where('id', $clientid)->first();
once you made this query you can easily call it like this:
$data=array(
"residence"=>$client->residence,
);
Now there are no problems with string conversion and arrays when you need to insert afterwards.

show tables get only the last name of table

I'm trying to show the name of table in my database. I write this code :
function affiche_liste()
{
$db=new PDO('mysql:host=localhost;dbname=testf','root','');
$result = $db->query("SHOW TABLES");
foreach($result->fetch(PDO::FETCH_NUM) as $data) {
$tableList = $data[0];
}
return $tableList;
}
It give to me only the last table ?
For a simple query without parameters and the SQL hard coded you can use a generic function passing the connection and SQL to the function.
The following function () returns an array containing all rows in the result set.
function queryAll($db,$query){
$sth = $db->query($query);
$result = $sth->fetchAll(PDO::FETCH_NUM);
return $result;
}
For a simple query without parameters and the SQL hard coded you can use a generic function passing the connection and SQL to the function.
The following function () returns an array containing all rows in the result set.
function queryAll($db,$query){
$sth = $db->query($query);
$result = $sth->fetchAll(PDO::FETCH_NUM);
return $result;
}
$db=new PDO('mysql:host=localhost;dbname=testf','root','');
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$query = "SHOW TABLES";
$tables = queryAll($db,$query);
print_r($tables);
Each time you are looping through the results, your are overwriting the variable tableList. Instead, you need to append to an array of results.
function affiche_liste()
{
$db=new PDO('mysql:host=localhost;dbname=testf','root','');
$result = $db->query("SHOW TABLES");
$tableList = array();
foreach($result->fetch(PDO::FETCH_NUM) as $data) {
array_push($tableList, $data[0]);
}
return $tableList;
}
try this
`
function affiche_liste()
{$db=new PDO('mysql:host=localhost;dbname=testf','root','');
$result = $db->query("SHOW TABLES");
foreach($result->fetch(PDO::FETCH_NUM) as $data) {
$tableList[] = $data[0];
}
return $tableList;
}
`
i hope it'll work...
here your array is overwitting every time...that's the reason you were getting the last table name....so you need to append to the existing array...

Add items to query result - Laravel

I'm slowly moving my API to Laravel and coming to grips with the Query Builder.
I'm trying to achieve this:
$data = array();
$query = "SELECT * FROM blog_posts WHERE post_type = 3 AND post_status = 1 ORDER BY id DESC";
$result = mysqli_query($cms_connection, $query);
if($result) {
while($row = mysqli_fetch_assoc($result)) {
$row['post_seo'] = seoUrl($row['post_title']);
$data['data'][] = $row;
}
$data['success'] = true;
$response = json_encode($data);
}
My problem isn't necessarily with getting the query, but as you can see I'm using the result of the query and then injecting it back into the final array.
So essentially, I'm fetching rows, transforming some of the attributes fetched, and then injecting the newly created attributes into the resulting array.
This is what I have so far:
$posts = DB::table('blog_posts')
-where(['post_type' => 1, 'post_status' => 1)
->orderBy('id', 'desc')
->take(5)->get();
You could do it this way
// get your data (yours part of code)
$posts = DB::table('blog_posts')
-where(['post_type' => 1, 'post_status' => 1])
->orderBy('id', 'desc')
->take(5)->get();
// add post_seo
foreach ($posts as $post) {
$post->post_seo = seoUrl($post->post_title);
}
// set result array
$data['data'] = $posts;
$data['success'] = true;
// response
$response = response()->json($data);
// or in case you want to return it just
return response()->json($data);
EDIT
You could do it also a bit better, using Eloquent. If you have such model (you need to add valid namespaces and use statements)
class BlogModel extends Model
{
protected $table = 'blog_posts';
protected $appends = ['post_seo'];
public function getPostSeoAttribute($value)
{
return seoUrl($this->post_title);
}
}
(added accessor to post_seo attribute and added post_seo to results when converting to array)
You can now do (shorter syntax than in previous example):
// get your data
$posts = BlogPost::where('post_type',1)
->where('post_status',1)
->orderBy('id', 'desc')
->take(5)->get();
// response
$response = response()->json(['data' => $posts, 'success' => true]);

JOIN on a subquery with Zend Framework 2 TableGateway

I'm trying to do a query with Zend Framework 2 where I have a SELECT inside a JOIN statement. So far, here's what I've tried, but injecting the SELECT object into the first parameter of join() doesn't seem to be working. I've resorted to such an approach since I need to order the results first before doing any grouping. Any ideas on how to get it working?
public function getSearchKeyword($keyword, $limit)
{
$select = $this->keywords->getSql()->select();
$subquery = $this->pages->getSql()->select();
$subWhere = new \Zend\Db\Sql\Where();
$subWhere->equalTo('delete_flag', 'n')
->equalTo('published_flag', 'y');
$subquery->where($subWhere);
$where = new \Zend\Db\Sql\Where();
$where->like('keyword', '%' . $keyword . '%')
->equalTo('delete_flag', 'n');
$select->columns(array('display' => 'keyword', 'url'))
->join(array('sub' => $subquery), 'sub.page_id = keywords.page_id', array())
->where($where)
->group(array('keywords.page_id', 'keywords.keyword'))
->order(array('rank', 'keyword'))
->limit($limit);
$row = $this->tableGateway->selectWith($select);
return $row;
}
The query I'm trying to write is below:
SELECT keywords.keyword AS display, keywords.url
FROM keywords
INNER JOIN
(
SELECT * FROM pages WHERE published_flag = 'y' AND delete_flag = 'n' ORDER BY page_id DESC
) pages
ON pages.page_id = keywords.page_id
WHERE published_flag = 'y'
AND delete_flag = 'n'
AND keywords.keyword LIKE '%?%'
GROUP BY display, page_id;
I was working around the same problem and did not found a standard way to solve it. So I got a working but not zf2 standard one
Create a small interface to mannage Db conections
Implements it as a small class to get a connection PDO object to
your database
execute your arbitrary querys
Code sample
// Filename: /module/MyTools/src/MyTools/Service/DbModelServiceInterface.php
namespace MyTools\Service;
interface DbModelServiceInterface
{
/**
* Will return the result of querying the curret database
*
* #param type $query
* #result mixed
*/
public function dbQuery($query);
/**
* Will return a connection object that links to curret database
*
* #result mixed
*/
public function getConnection();
}
The class implementing the interface. It creates and offers a PDO connection. Note: It needs extra code to close conns and to perfeorm security adm...
It test it and is completely functional.
code:
// Filename: /module/MyTools/src/MyTools/Service/DbModelServiceMySql.php
namespace MyTools\Service;
use MyTools\Service\DbModelServiceInterface;
use PDO;
class DbModelServiceMySql implements DbModelServiceInterface
{
protected $driverConfig;
protected $connection;
protected $isconnected = FALSE;
protected $dbname = '';
/**
* Creates a connection to main database
*/
public function __construct()
{
$driverConfig = self::getDriverDef();
$this->driverConfig = $driverConfig; // new PDO($driverConfig['dsn'], $driverConfig['username'], $driverConfig['password']);
$this->_connect();
}
protected function _connect(){
$dsn = (isset($this->driverConfig['dsn'])) ? $this->driverConfig['dsn'] : '';
$username = (isset($this->driverConfig['username'])) ? $this->driverConfig['username'] : '';
$password = (isset($this->driverConfig['password'])) ? $this->driverConfig['password'] : '';
if( ($dsn) && ($username) && ($password)){
$options = [PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES utf8', ];
try {
$this->connection = new PDO($dsn, $username, $password, $options);
$this->isconnected = TRUE;
$this->_setdbname($dsn);
} catch (Exception $ex) {
throw new RuntimeException('YOUR ERROR MESSAGE.');
}
}
return $this->isconnected;
}
protected function _setdbname($dsn){
if($dsn){
$chunks = explode(';', ''.$dsn);
foreach($chunks as $chunk){
if(strpos('***'.$chunk, 'dbname') > 2){
$nombre = explode('=', $chunk);
$this->dbname = $nombre[1];
break;
}
}
}
}
/**
* {#inheritDoc}
*/
public function dbQuery($query) {
if($this->connection){
$resultset = $this->connection->query($query);
if($resultset){
return $resultset->fetchAll(PDO::FETCH_ASSOC);
}else{
return ['Error' => 'YOUR CUSTOM ERROR MESSAGE.'];
}
}else{
return ['Error' => 'OTHER CUSTOM ERROR MESSAGE'];
}
}
public static function getDriverDef()
{
$autoloadDir = __DIR__ . '../../../../../../config/autoload/';
$credentialsdb = include $autoloadDir . 'local.php';
$globaldb = include $autoloadDir . 'global.php';
$def = (isset($globaldb['db'])) ? $globaldb['db'] : array();
$credentials = (isset($credentialsdb['db'])) ? $credentialsdb['db'] : $credentialsdb;
return array_merge($def, $credentials);
}
/**
* {#inheritDoc}
*/
public function getConnection() {
if($this->connection){
return $this->connection;
}else{
return 'Error: YOUR CUSTOM ERROR MESSAGE';
}
}
/**
* {#inheritDoc}
*/
public function getDbName(){
return $this->dbname;
}
}
Now you have a class you can instantiate elsewhere to perform the querys you need.
use:
code:
$myQuery = 'the very very complex query you need to execute'
$myDbConn = new MyTools\Service\DbModelServiceMySql();
$result = $myDbConn->dbQuery($myQuery);
If success you got a resulset array of pairs columnName => value
You can try this one.
$select->columns(array('display' => 'keyword', 'url'))
->join(array('sub' => 'pages'), 'sub.page_id = keywords.page_id',
array(), $select::JOIN_INNER)
->where($where)
->group(array('keywords.page_id', 'keywords.keyword'))
->order(array('rank', 'keyword'))
->limit($limit);
In your code, you are getting all keywords which page_id's is in sub page_id where delete_flag = 'n' and published_flag = 'y'.
join(..., 'sub.page_id = keywords.page_id', array())
When you don't need any columns of pages table, you can use IN instead of JOIN.
For example when you need to know which keywords are in which pages, you should use JOIN, but when you need to know which keyboards are in any pages, you can use IN statement.
Anyway :
There is no standard way in ZF2 but you can try following code.
public function getSearchKeyword($keyword, $limit)
{
$select = $this->keywords->getSql()->select();
$subquery = $this->pages->getSql()->select();
$subWhere = new \Zend\Db\Sql\Where();
$subWhere->equalTo('delete_flag', 'n')
->equalTo('published_flag', 'y');
$subquery->columns(array('page_id'))
->where($subWhere);
$where = new \Zend\Db\Sql\Where();
$where->like('keyword', '%' . $keyword . '%')
->equalTo('delete_flag', 'n')
->in('keywords.page_id', $subquery);
$select->columns(array('display' => 'keyword', 'url'))
->where($where)
->group(array('keywords.page_id', 'keywords.keyword'))
->order(array('rank', 'keyword'))
->limit($limit);
$row = $this->tableGateway->selectWith($select);
return $row;
}
I've faced a similar issue. Since the FROM table and Subquery's FROM table were different i got an error.
My workaround was to extract the SQL and create a statement.
$sql = $select->getSqlString(new \Zend\Db\Adapter\Platform\Mysql());
$stmt = $this->getAdapter()->createStatement($sql);
$stmt->prepare($sql);
$result = $stmt->execute();
$resultSet = new ResultSet(); \\ Class Zend\Db\ResultSet\ResultSet
$resultSet->initialize($result);

Laravel 4 Method Improvement

I have this index method:
public function index()
{
// In the view, there are several multiselect boxes (account managers, company names and account types). This code retrives the values from the POST method of the form/session.
$company_names_value = Input::get('company_names_value');
$account_managers_value = Input::get('account_managers_value');
$account_types_value = Input::get('account_types_value');
// If there has been no form submission, check if the values are empty and if they are assign a default.
// Essentially, all of the records in the table column required.
if (is_null($company_names_value))
{
$company_names_value = DB::table('accounts')
->orderBy('company_name')
->lists('company_name');
}
if (is_null($account_managers_value))
{
$account_managers_value = DB::table('users')
->orderBy(DB::raw('CONCAT(first_name," ",last_name)'))
->select(DB::raw('CONCAT(first_name," ",last_name) as amname'))
->lists('amname');
}
if (is_null($account_types_value))
{
$account_types_value = DB::table('account_types')
->orderBy('type')
->lists('type');
}
// In the view, there is a dropdown box, that allows the user to select the amount of records to show per page. Retrieve that value or set a default.
$perPage = Input::get('perPage', 10);
// This code retrieves the order from the session that has been selected by the user by clicking on a table column title. The value is placed in the session via the getOrder() method and is used later in the Eloquent query and joins.
$order = Session::get('account.order', 'company_name.asc');
$order = explode('.', $order);
// Here we perform the joins required and order the records, then select everything from accounts and select their id's as aid. Then whereIn is used to select records where company name, account manager name and account type matches the values of the multiselect boxes or the default set above.
$accounts_query = Account::leftJoin('users', 'users.id', '=', 'accounts.user_id')
->leftJoin('account_types', 'account_types.id', '=', 'accounts.account_type_id')
->orderBy($order[0], $order[1])
->select(array('accounts.*', DB::raw('accounts.id as aid')));
if (!empty($company_names_value)) $accounts_query = $accounts_query->whereIn('accounts.company_name', $company_names_value);
$accounts = $accounts_query->whereIn(DB::raw('CONCAT(users.first_name," ",users.last_name)'), $account_managers_value)
->whereIn('account_types.type', $account_types_value)
->paginate($perPage)->appends(array('company_names_value' => Input::get('company_names_value'), 'account_managers_value' => Input::get('account_managers_value'), 'account_types_value' => Input::get('account_types_value')));
$accounts_trash = Account::onlyTrashed()
->leftJoin('users', 'users.id', '=', 'accounts.user_id')
->leftJoin('account_types', 'account_types.id', '=', 'accounts.account_type_id')
->orderBy($order[0], $order[1])
->select(array('accounts.*', DB::raw('accounts.id as aid')))
->get();
$message = Session::get('message');
$default = ($perPage === null ? 10 : $perPage);
$this->layout->content = View::make('admin.accounts.index', array(
'accounts' => $accounts,
'accounts_trash' => $accounts_trash,
'company_names' => DB::table('accounts')->orderBy('company_name')->lists('company_name', 'company_name'),
'account_managers' => DB::table('users')->orderBy(DB::raw('CONCAT(first_name," ",last_name)'))->select(DB::raw('CONCAT(first_name," ",last_name) as amname'))->lists('amname', 'amname'),
'account_types' => DB::table('account_types')->orderBy('type')->lists('type', 'type'),
'perPage' => $perPage,
'message' => $message,
'default' => $default
));
}
Basically, I am building a query that searches several tables (hence the joins). In the view a user has the ability to select multiple values from various multi-select boxes and then submit a form which will then populate the $company_names_value, $account_managers_value and $account_types_value variables.
Initially, when there is no form submission, I'm using Query Builder to select all records for each type, and then using them in the query.
It works but it is slow and messy. I was wondering if any of you Laravel 4 gurus could help me improve it further, so that the queries are faster and the code is lighter.
Thanks in advance.
This has now been refactored significantly, and it is very fast now. I've moved most of my code in to my models, as well as refactoring that code.
Here's the new index method:
public function index()
{
$account = explode(',', Input::get('account'));
$account_manager = explode(',', Input::get('account_manager'));
$account_type = explode(',', Input::get('account_type'));
$perPage = Input::get('perPage', 10);
$order = Session::get('account.order', 'company_name.asc');
$order = explode('.', $order);
$accounts = Account::accounts($order, $account, $account_manager, $account_type)->paginate($perPage)->appends(array(
'account' => Input::get('account'),
'account_manager' => Input::get('account_manager'),
'account_type' => Input::get('account_type'),
'perPage' => Input::get('perPage')
));
$accounts_trash = Account::accountsTrash($order)->get();
$message = Session::get('message');
$default = ($perPage === null ? 10 : $perPage);
$this->layout->content = View::make('admin.accounts.index', compact('accounts', 'accounts_trash', 'message', 'default'));
}
And the new getAccountByName() method in my controller that is used during my AJAX call. This should probably go in the model:
public function getAccountByName()
{
$name = Input::get('account');
return Account::select(array('id', DB::raw('company_name as text')))->where('company_name', 'like', "%$name%")->get();
}
And finally two new methods in my model for retrieving accounts and accounts trash:
public function scopeAccounts($query, $order, $account, $account_manager, $account_type)
{
$query->leftJoin('users', 'users.id', '=', 'accounts.user_id')
->leftJoin('account_types', 'account_types.id', '=', 'accounts.account_type_id')
->orderBy($order[0], $order[1])
->select(array('accounts.*', DB::raw('accounts.id as aid')));
if (!empty($account[0])) {
$query = $query->whereIn('accounts.id', $account);
}
if (!empty($account_manager[0])) {
$query = $query->whereIn('users.id', $account_manager);
}
if (!empty($account_type[0])) {
$query = $query->whereIn('account_types.id', $account_type);
}
}
public function scopeAccountsTrash($query, $order)
{
$query->onlyTrashed()
->leftJoin('users', 'users.id', '=', 'accounts.user_id')
->leftJoin('account_types', 'account_types.id', '=', 'accounts.account_type_id')
->orderBy($order[0], $order[1])
->select(array('accounts.*', DB::raw('accounts.id as aid')));
}
Again, there's probably a ton of things to get tidied up here but I'm certainly closer to a much faster and cleaner solution. Doing it like this has reduced the loading times from 12 seconds to 234ms.