Laravel/SQL: How to fetch data from multiple table in a single query? that too using 'where' - sql

Working on a search functionality on Laravel App(Blog/Posts).
There are multiple types of posts (each having a separate table in the database)
Like Business posts, Social Life posts etc..
Below is the search function on SearchController
class SearchController extends Controller
{
public function search(Request $request, $query = null)
{
if($query == null)
return redirect()->route('home');
$search = Business::where([['title','like','%'.$query.'%'],['status','=',1]])
->orWhere([['description','like','%'.$query.'%'],['status','=',1]])
->paginate(10);
return view('front.search',[
'results' => $search,
'query' => $query
]);
}
}
So basically my question is how to add other types of Post's table also?
My main motive is that when someone searches for anything, the result should be fetched from all types of posts table(business, nature, life & so on..).

You have to maintain common id in both the table
NOTE: Join is the preferable method
$querys = DB::table('Business')->where([['Business.title','like','%'.$query.'%'],['Business.status','=',1]])
->orWhere([['Business.description','like','%'.$query.'%'],['Business.status','=',1]]);
$querys->join('socialtable','socialtable.userid','=','Business.userid');
// Just join the social table
$querys->where('socialtable.title', 'like','%'.$query.'%');
$result = $querys->paginate(10);

If you have a model called Book, like this:
class Book extends Model
{
/**
* Get the author that wrote the book.
*/
public function author()
{
return $this->belongsTo('App\Author');
}
}
Then you can retrieve all of your books with authors like this:
$books = App\Book::with(['author'])->get();
Check out Eager loading from Laravel documentation.

Just add table name before every field
$querys = DB::table('Business')->where([['Business.title','like','%'.$query.'%'],['Business.status','=',1]])
->orWhere([['Business.description','like','%'.$query.'%'],['Business.status','=',1]]);
$querys->join('socialtable','socialtable.userid','=','Business.userid');
// Just join the social table
$querys->where('socialtable.title', 'like','%'.$query.'%');
$result = $querys->paginate(10);

Related

Laravel 8 left join where clause is ambiguous

I'm trying to use leftJoin but I'm having problem with conflicts between equal names in both tables.
Tables
products: added_by - user_id - published - approved - featured
product_types: added_by - user_id - published - approved - featured
$products = ProductType::
leftJoin('products', 'products.product_type_id', '=', 'product_types.id')
->select('product_types.*')
How to solve this problem?
You should start by implementing the correct relationships, it will help you in the future.
class ProductType extends Model {
public function products(): HasMany
{
return $this->hasMany(Product::class);
}
// Add the other missing relations too (user, etc)
}
class Product extends Model {
public function productType(): BelongsTo
{
return $this->belongsTo(ProductType::class);
}
// Add the other missing relations too (user, etc)
}
You can now use:
// Query all Product Types with related products
$productTypes = ProductType::with('products')->get();
// Query all ProductTypes that have products
$productTypes = ProductType::withWhereHas('products')->get();
/** #var ProductType $productType */
foreach ($productTypes as $productType) {
// You have access to the collection of associated products, in memory
$productType->products;
}
More information on eloquent relationships in https://laravel.com/docs/9.x/eloquent-relationships

Laravel 5.5, Display news data from two tables

I am new to laravel and I experience some trouble. I try to obtain data stored in two different tables and display them:
News.php (model)
public static function Data($category) {
$perPage = config('var.news.perPage');
if ($category) {
$news = News::orderBy('id', 'desc')->where('category', $category)->SimplePaginate($perPage);
} else {
$news = News::orderBy('id', 'desc')->SimplePaginate($perPage);
}
return $news;
}
This is how I grab all data from News table which struct is:
id, title, body, created_at updated_at, created_by, updated_by, category
The category column contains values separated by comma, e.g. 1,2,3,4
Now, I have another table, News_Cat which has id, name columns.
In another method I try to grab the filters names against values stored in category column of News table
public static function getFilterNames($id) {
$filters = DB::table('News_Cat')
->select('News_Cat.name as name')
->leftJoin('News', DB::raw('CAST(News_Cat.id as nvarchar)'), DB::raw('ANY(SELECT(News.category))'))
->where('News.id', $id)
->get();
return $filters;
}
However, it completely does not work. What I try to achieve is to display filter name in view.blade as 'name' value for specified filter from News_Cat
#if($news->count())
#foreach($news as $article)
<a href="{{ route('news.show', $article->id) }}" class="item angled-bg" data-filters="{{ $filters }}">
<div class="row">
So as result I would get e.g. data-filters="news, update, hot, latest"> instead data-filters="1,2,3,4">
Thank you
You should use eloquent!
In your News Model
public function getFiltersAttribute(){
$categories = explode(',', $this->category);
return implode(', ', NewsCat::find($categories)->pluck('name')->toArray());
}
then in your view :
{{ $article->filters }}
will output news, update, hot, latest
BUT
You should use a pivot table between your categories and your news, it would be much easier.
This method can't allow you to eager load the relationship and make a request for each news
If you can't change your database structure, I can propose you this:
In the boot method of your AppServiceProvider:
Config::set('tags', NewsCat::all());
THEN
public function getFiltersAttribute(){
$categories = explode(',' $this->category);
return implode(', ', config('tags')->whereIn('id', $categories)->pluck('name')->toArray());
}
MANY TO MANY METHOD
I am using laravel naming convention for the table :
news, categories_news (the pivot), and categories
You will have 2 models : New and Category
In your New Model
public function categories(){
return $this->belongsToMany(Category::class)
}
in your Category Model :
public function news(){
return $this->belongsToMany(New::class);
}
if you are not using laravel naming conventions, you will have to customize these raltionship like this : https://laravel.com/docs/5.5/eloquent-relationships#many-to-many

Codeigniter populate nested associations

this is the results i need from the database
{comment: 'hello there', user: [{name: 'sahan', id: 1}], id: 2}
function used to get comments
public function get_comments(){
$query = $this->db->get('comments');
return $query->result_array();
}
I have a comments table and a users table, When a user comments on something the
comment is saved as follows
comment > Comment text
user: userid
So when the data is shown I need codeigniter to populate the user field with the user data found from the users table
Does anyone know how to do this ?
I used this functionality in SailsJS but dont know how to do it here in CodeIG
Sails.js populate nested associations
Codeigniter 's active record is not as advanced as SailJS active record, but you can achieve what you are asking for with two queries within the same method.
I'm assuming the comments table has a foreign key to the users table through a user_id field.
public function get_comments() {
/* Get all the comments */
$query = $this->db->get('comments');
$comments = $query->result_array();
/* Loop through each comment, pulling the associated user from the db */
foreach $comments as &$comment {
$query = $this->db->get_where('users', array('id' => $comment['user_id']));
$user = $query->result_array();
/* Put the user's data in a key called 'user' within the comment array */
$comment['user'] = $user;
/* Remove the unnecessary user_id and $user variable */
unset($comment['user_id']);
unset($user);
}
return $comments;
}

CRUD very slow with conditions, is there any other faster way?

im having problem with CRUd now that i filled the database. CRUD is taking ages to show, becouse it takes condition from M:M tables.
Tables:
Table USER. has many labels (hasMany)
Table LABLE, has many users (hasMany)
Intermidiate Table UserLabel, has two hasOne
I want to show all users from some label with CRUD like this:
MODEL USER:
class Model_User extends Model_Table {
public $table ='user';
function init(){
parent::init();
$this->addField('fbid')->mandatory('Facebook id required');
...
$this->hasOne('Application');
$this->hasMany('UserLabel');
$this->addExpression('ratio')->set(function($model,$select){
return $select->expr('ROUND(([f2] / [f1]) * 100,0)')
->setCustom('f1',$model->getElement('sends'))
->setCustom('f2',$model->getElement('clicked'));
});
$this->addHook('beforeSave',function($m){
$m['updated']=$m->dsql()->expr('now()');
});
}
MODEL LABEL:
class Model_Label extends Model_Table {
public $table ='label';
function init(){
parent::init();
$this->addField('name')->mandatory('Name required');
$this->addFIeld('application_id')->refModel('Model_Application')->defaultValue($this->api->recall('app'))->system(true);
$this->addField('active')->type('boolean')->defaultValue('true')->system(true);
$this->addField('created')->type('timestamp')->defaultValue($this->dsql()->expr('now()'))->system(true);
$this->addField('updated')->type('timestamp')->system(true);
$this->hasMany('UserLabel');
$m = $this->add("Model_UserLabel");
$this->addExpression("users", $m->dsql()
->field($m->dsql()->expr("count(*)"), "all users")
->where("label_id", $this->getField("id"))
);
MODEL USER LABEL
class Model_UserLabel extends Model_Table {
public $table ='userlabel';
function init(){
parent::init();
$this->hasOne('User');
$this->hasOne('Label');
}
}
CODE FOR CRUD
$c = $this->add('CRUD');
$c->setModel('User', array('name', 'gender','country','city'));
$c->model->addCondition('id','in',
$this->add('Model_UserLabel')->addCondition('label_id', $_GET['l'])->dsql()->field('user_id')
);
Is there any better way to do this?
ps. I tested this solution, it is a lot faster but still very slow at around > 5.000 users:
//get all users
$records = $this->api->db->dsql()->option('distinct')->table('user')->join('userlabel.user_id')->field('user.id')->where('userlabel.label_id',$_GET['l'])->do_getAll();
foreach($records as $record){
$users .= ','.$record['id'];
}
//create CRUD
$c = $this->add('CRUD');
$c->setModel('User', array('name', 'gender','country','city','sends','clicked','ratio'));
$c->model->addCondition("application_id", $this->api->recall('app'));
$c->model->addCondition('id','in',
'('.$users.')'
);
Source code express more than words, so you better add your model definition source code (maybe not full) in your question.
What should be one row in your CRUD/Grid? I guess it's not 1 user = 1 row, but 1 user_label should be one row in grid. So you should set UserLabel model as model for your grid.
And then define some additional fields in Model_UserLabel by joining them from user and/or label tables directly like this:
class Model_UserLabel extends SQL_Model {
function init() {
parent::init();
// ...
// fields from user table
$join_u = $this->join('user', 'user_id');
$join_u->addField('username'); // this adds fields in current model from joined table
$join_u->addField('email');
// fields from label table
$join_l = $this->join('label', 'label_id');
$join_l->addField('name');
}
}
Note: source code above is untested and put here only as example.
EDIT:
Try this solution - almost the same as I wrote earlier above:
MODEL USER LABEL
class Model_UserLabel extends Model_Table {
public $table ='userlabel';
function init(){
parent::init();
$this->hasOne('User');
$this->hasOne('Label');
// join user table and add fields to this model from joined user table
$j = $this->join('user', 'user_id');
$j->addField('name');
$j->addField('gender');
$j->addField('country');
$j->addField('city');
}
}
CODE FOR CRUD
$m = $this->add('Model_UserLabel'); // UserLabel here not User
$m->addCondition('label_id', $_GET['l']); // and then this is simple
$c = $this->add('CRUD');
$c->setModel($m, array('name', 'gender','country','city'));
Try this solution and as (almost) always - code is untested.
EDIT:
Please try this version - is it working faster? That's basically your P.S. example, but you shouldn't extract all user IDs, join them and then create huge select with a lot of 'in'.
Faster result should be if you could do all with just one DB request without any additional processing of data.
// parameters
$app_id = $this->api->recall('app);
$label_id = $_GET['l'];
// prepare model for grid
$m = $this->add('Model_User'); // default User model
$m->_dsql()->option('distinct') // add join to userlabel table + conditions
->join('userlabel.user_id')
->where('userlabel.label_id', $label_id)
->where($m->getField('application_id'), $app_id);
// create CRUD and set it's model. All conditions already set on model above
$c = $this->add('CRUD');
$c->setModel($m, array('name', 'gender','country','city','sends','clicked','ratio'));
NOTE: Source code above as often - untested :)

Preserve Order of IN in ORM Order

I'm trying to do a query where I preserve the order of the ids in a IN statement. I can't seem to do it with either the Model Manage Query Builder or the standard ORM 'order' array parameter. Am I missing something? I keep getting:
UNEXPECTED TOKEN IDENTIFIER(, NEAR TO 'id`enter code here`,17743,16688,16650
Here's my model manager:
$query = $this->modelsManager->createQuery('SELECT * FROM Projects WHERE id IN ('.implode(',', array_keys($finalIterations)).')
ORDER BY FIELD(id,'.implode(',', array_keys($finalIterations)).'');
It's pretty obvious PhQL doesn't like the FIELD key word. Is there a way for me to do what I'm trying to do with PhQL? It seems I will not be able to do what I need to.
Unfortunately as previously said, this is missing a feature in Phalcon.
Have a look at this function, I've put it into my ModelBase abstract class which is parent class of all my models. It uses PhQL variable binding, so it's safe for handling direct user input.
You could have reimplemented custom \Phalcon\Mvc\Model\Criteria but this solution seems to be easier to work with, at least for me.
ModelBase abstract
public function appendCustomOrder( \Phalcon\Mvc\Model\CriteriaInterface &$criteria, $orderField, array &$orderValues = [] ) {
if(!empty($orderValues)) {
$queryKeys = $bindParams = [];
foreach($orderValues as $key => $id) {
$queryKey = 'pho'.$key;
$queryKeys[] = ':'.$queryKey.':';
$bindParams[$queryKey] = $id;
}
// TODO: add support for multiple orderBy fields
$criteria->orderBy('FIELD('.$orderField.','.implode(',',$queryKeys).')');
// there's no 'addBind' function, need to merge old parameters with new ones
$criteria->bind( array_merge( (array) #$criteria->getParams()['bind'], $bindParams ) );
}
}
Controller usage
$projectIDs = [17743, 16688, 16650];
$projectsModel = new Projects();
$criteria = $projectsModel->query->inWhere( 'id', $projectIDs );
$projectsModel->appendCustomOrder( $criteria, 'id', $projectIDs );
$projectsData = $criteria->execute();
This will generate valid PhQL syntax similar to this one:
SELECT `projects`.`id` AS `id`, `projects`.`title` AS `title`
FROM `projects`
WHERE `projects`.`id` IN (:phi0, :phi1, :phi2)
ORDER BY FIELD(`projects`.`id`, :pho0, :pho1, :pho2)