I'm working on a filter for some products. I have the majority of it working however I am encountering an error with an impossible where clause.
The table contains multiple rows for a single product and I am trying to match multiple criteria per product, which is causing it to fail.
If you have an opinion on this, or possibly a way to fix this, I would greatly appreciate it.
The database table looks like this:
--------------------------------------------
|id | FilterKey | filterValue | product_id |
--------------------------------------------
|1 | Colour | Gunmetal | 1 |
|2 | Colour | Silver | 1 |
|3 | Size | 750cc | 1 |
|4 | Size | 1000cc | 1 |
|5 | Colour | Red | 2 |
|6 | Colour | Blue | 2 |
|7 | Size | 750cc | 2 |
|8 | Size | 1000cc | 2 |
--------------------------------------------
And the filter looks like this:
public function scopeFilterProduct($query, $filters)
{
$this->filters = $filters;
if (count ($this->filters) === 1 && isset($this->filters[0]))
{
return $query;
}
$query->join('product_filters', 'products.id', '=', 'product_filters.product_id')->Where(function($query){
foreach ($this->filters as $filter => $vals)
{
$this->filter = $filter;
$this->vals = $vals;
$query->Where(function ($query){
$query->Where('filterKey', $this->filter);
$query->Where(function($query){
foreach ($this->vals as $val){
$query->orWhere('filterValue', $val);
}
$this->vals = null;
});
});
$this->filter = null;
};
});
return $query;
}
This then outputs the following SQL statement:
select
distinct
`products`.`id`
, `product_id`
from
`products`
inner join
`product_filters`
on
`products`.`id` = `product_filters`.`product_id`
where
(
(`filterKey` = 'Colour' and (`filterValue` = 'gunmetal'))
and
(`filterKey` = 'Size' and (`filterValue` = '750cc'))
)
and
`products`.`deleted_at` is null
If selected, as in the screenshot, then only 'product one' should be present on the page.
The scope you have added in my opinion is wrong. Even your database structure is incorrect in my opinion. Here is how i would structure this:
Filters Table
This model will hold all the filter values. For example, Colour, Size etc. Here is how the filter table will be structured:
-----------------
|id | name |
-----------------
|1 | Colour |
|2 | Size |
-----------------
So your eloquent model become something like this:
class Filter extends Model
{
protected $fillable = ['id', 'name'];
public function products()
{
return $this->belongsToMany(Product::class, 'products_filters');
}
}
Products Table
Your product models becomes:
class Product extends Model
{
public function filters()
{
return $this->belongsToMany(Filter::class, 'products_filters');
}
}
products_filters Table
After the above changes, here is how the table will be structured:
--------------------------------------------
|id | filter_id | filterValue | product_id |
--------------------------------------------
|1 | 1 | Gunmetal | 1 |
|2 | 1 | Silver | 1 |
|3 | 2 | 750cc | 1 |
|4 | 2 | 1000cc | 1 |
|5 | 1 | Red | 2 |
|6 | 1 | Blue | 2 |
|7 | 2 | 750cc | 2 |
|8 | 2 | 1000cc | 2 |
--------------------------------------------
Now you can simply query the filters table, then get associated products for all filters. After that you simply need to compile a list of unique products.
Unqiue products based on selected filters.
$ids = [];
$products = new \Illuminate\Support\Collection();
foreach($filters as $filter) {
foreach($filter->products as $product) {
if(!in_array($product->id, $ids)) {
$ids[] = $product->id;
$products->push($product);
}
}
}
return view('results', compact('products'));
In your view, you need to write:
#foreach($products as $product)
// Your product block HTML
#endforeach
Related
I am currently trying to make a relation between 3 tables.
post
id
name
category
id
name
post_category
id
post_id
category_id
Database
post
| 1 | post1 |
| 2 | post2 |
| 3 | post3 |
category
| 1 | cat1 |
| 2 | cat2 |
| 3 | cat3 |
post_category
| 1 | 1 | 1 |
| 2 | 2 | 1 |
| 3 | 3 | 2 |
| 3 | 2 | 2 |
| 3 | 1 | 3 |
Model Post.php
public function getCategory()
{
return $this->belongsToMany(Category::class, 'post_category');
}
PostController.php
$data = Post::with('getCategory')->get();
It returns correct post list.
Now i want to filter the post by category. I try, but it not working
$categoryId = [1,2];
$data = Post::with('getCategory')->whereHas('category', function ($query) use ($categoryId) {
$query->whereIn('id', $categoryId);
})->orderBy('id','DESC')->get();
please help me
use Laravel 5.4
apparently everything is fine!
One suggestion is to add two more parameters to the belongsToMany method, like:
public function getCategory()
{
return $this->belongsToMany(Category::class, 'post_category', 'post_id', 'category_id');
}
https://laravel.com/api/7.x/Illuminate/Database/Eloquent/Concerns/HasRelationships.html#method_belongsToMany
You should rename your getCategory() function to simply category(). That makes the relation names much more straightforward and probably fixes your issue.
THEN, you should be able to call whereHas('category', ...).
If it still does not work, simply chain a ->toSql() to any of your queries and debug the actual query that way.
I have 2 database tables 'super_admin_staff' and 'super_admin_roles'
The staff table is made up like :
id
name
email
phone
role
The role table is made up like :
id
role
The id column from the roles table is unique and is associated with the 'roles' column of the staff table.
At the moment i am fetching all the roles from the database using model :
function superAdminStaffRoles()
{
$this->db->select('*');
$this->db->from('super_admin_roles');
$query = $this->db->get();
$result = $query->result();
return $result;
}
In my controller i pass it over to my view like this :
public function superAdminStaffRoles()
{
if ($this->isSuperAdmin() != true) {
$this->loadThis();
} else {
$this->load->model('super_admin_staff_model');
$data['userRecords'] = $this->super_admin_staff_model->superAdminStaffRoles();
$this->global['pageTitle'] = 'Staff Roles';
$this->global['pageDesc'] = 'Add or Edit new Staff Roles and Permissions';
$this->loadViews("super_admin/staff_roles", $this->global, $data, null);
}
}
then display the data in my view like :
<tbody>
<?php
if (!empty($userRecords)) {
foreach ($userRecords as $record) {
?>
<tr>
<td><?php echo $record->role ?></td>
<td>where want to loop through the association</td>
<td class="text-center">
<a class="btn btn-sm btn-info" href="<?php echo base_url() ?>/EditRole/<?php echo $record->id; ?>"><i class="fas fa-edit"></i></a>
<a class="btn btn-sm btn-danger deleteUser" href="<?php echo base_url() ?>/DeleteRole/<?php echo $record->id; ?>"><i class="fas fa-trash"></i></a>
</td>
</tr>
<?php
}
}
?>
</tbody>
How can i fetch the additional information i need in my model to display how many staff are associated with that role? i think i may need to join the tables and i don't have much knowledge on joins.
can it be done in the same query?
the result should be something like :
Staff table
|id | name | role |
.......................
|1 | staff1 | 1 |
|2 | staff2 | 1 |
|3 | staff3 | 2 |
|4 | staff4 | 3 |
|5 | staff5 | 3 |
Role Table
|id | role |
...............
|1 | role 1 |
|2 | role 2 |
|3 | role 3 |
and the result should look like :
|Staff Role | Assigned |
........................
| Role1 | 2 |
| Role2 | 1 |
| Role3 | 2 |
Any help appreciated
Here you go
First, you need to join the two tables
INNER JOIN
$this->db->from('super_admin_staff');
$this->db->join('super_admin_roles', 'super_admin_staff.role = super_admin_roles.id');
You'll get the following
|id | name | role |id | role |
......................................
|1 | staff1 | 1 |1 | role 1 |
|2 | staff2 | 1 |1 | role 1 |
|3 | staff3 | 2 |2 | role 2 |
|4 | staff4 | 3 |3 | role 3 |
|5 | staff5 | 3 |3 | role 3 |
LEFT JOIN
$this->db->from('super_admin_staff');
$this->db->join('super_admin_roles', 'super_admin_staff.role = super_admin_roles.id', 'left');
You'll get the following
|id | name | role |id | role |
......................................
|1 | staff1 | 1 |1 | role 1 |
|2 | staff2 | 1 |1 | role 1 |
|3 | staff3 | 2 |2 | role 2 |
|4 | staff4 | 3 |3 | role 3 |
|5 | staff5 | 3 |3 | role 3 |
|6 | staff6 | 0 |null| null |
GROUP BY
Then grouping (which is strongly associated with the select because what is in the select clause MUST be in the group by).
Here you want to group by the role, you can do by grouping with super_admin_staff.role, super_admin_roles.id or super_admin_roles.role.
As you want the super_admin_roles.role in the output, let's take it as grouping column.
Using COUNT(*) will return the number of rows for each group.
$this->db->select('super_admin_roles.role as StaffRole, COUNT(*) as Assigned');
$this->db->from('super_admin_staff');
$this->db->join('super_admin_roles', 'super_admin_staff.role = super_admin_roles.id');
$this->db->group_by('super_admin_roles.role');
$query = $this->db->get();
Here is my table structure:
// tickets
+----+------------+----------------------+--------+---------+-------------------+
| id | subject | content | closed | user_id | unique_product_id |
+----+------------+----------------------+--------+---------+-------------------+
| 1 | subject1 | question1 | 0 | 123 | 2 |
+----+------------+----------------------+--------+---------+-------------------+
// unique_product
+----+---------------+------------+
| id | serial_number | product_id |
+----+---------------+------------+
| 1 | 2342rd34fc | 3 |
| 2 | fg34gt4r5t | 1 |
| 3 | 34ffvv4et6 | 3 |
+----+---------------+------------+
// products
+----+--------------+
| id | name |
+----+--------------+
| 1 | Router-rb51 |
| 2 | Switch-sfx2 |
| 3 | Router-rb300 |
+----+--------------+
Now I have a collection of tickets like this:
$tickets = tickets::where(user_id, "$user_id")->get();
foreach( $tickets as $ticket ){
$ticket->{I need to get the name of product here}
}
I can write a relation in the tickets model like this:
public function unique_product()
{
return $this->hasOne(unique_product::class, 'id', 'unique_product_id');
}
And I need one more relation to the products table for getting the name of product (i.e. Switch-sfx2). How should I write that relation?
$tickets = tickets::where(user_id, "$user_id")->with('unique_product.product')->get();
You can take advantage eager loading using with().
but for using with('unique_product.product') You have to define the relation ships between unique_products and products.
In your UniqueProduct model create a new relationship
public function product()
{
return $this->BelongsTo(Product::class, 'id', 'product_id');
}
After that you can access the column of a name like
foreach( $tickets as $ticket ){
$ticket->unique_product->product->name
}
So I have the following match table which contains the numbers of the teams that participated in that match. I want to set up a relationship with the teams which looks something like this:
Teams Table
| id | number | name | etc |
| 1 | 1234 | Example | etc |
| 2 | 2345 | Example | etc |
etc...
Matches Table
| id | match | red1 | red2 | blue1 | blue2 |
| 1 | 1 | 1234 | 1710 | 673 | 2643 |
| 2 | 2 | 2345 | 1677 | 4366 | 246 |
etc...
I want to have something like $this->match->where("match", "=", "2")->first()->teams();.
I have tried using hasMany() but I can't seem to get to use the red1, red2, blue1, blue3 columns.
What I have tried:
class Matches extends Model
{
protected $table = "match_table";
protected $fillable = [
"match_id",
"time",
"bluescore",
"redscore",
"red1",
"red2",
"red3",
"blue1",
"blue2",
"blue3",
];
public function teams()
{
return $this->hasMany("App\Models\Teams", "number", ["red1", "red2", "blue1", "blue2"]);
}
}
What I ended up doing was just looping through each column I wanted and then just returning a new Collection with the results in it.
public function teams()
{
$result = [];
foreach($this::$teamColumns as $column) {
$result[] = $this->hasMany("App\Models\Teams", "number", $column)->first();
}
return new Collection($result);
}
I have tables illustrated below
//reference type table
+---+-----------+---------+
|ID |Article_ID |Ref_Types|
+---+-----------+---------+
| 1 | 1 | article |
| 2 | 1 | book |
| 3 | 1 | article |
| 4 | 1 | article |
| 5 | 2 | book |
+---+-----------+---------+
//book references table
+---+-----------+--------+
|ID |Article_ID |Title |
+---+-----------+--------+
| 1 | 1 | book1 |
| 2 | 1 | book2 |
| 3 | 2 | book3 |
| 4 | 2 | book4 |
| 5 | 2 | book5 |
+---+-----------+--------+
//article references table
+---+-----------+-----------+
|ID |Article_ID |Title |
+---+-----------+-----------+
| 1 | 1 | article1 |
| 2 | 1 | article2 |
| 3 | 2 | article3 |
| 4 | 2 | article4 |
| 5 | 2 | article5 |
+---+-----------+-----------+
I have to look into first table and check the reference, of which type it is;
for each reference type, I have get reference table from related table
I have to output in order, as shown in table one.
1:
$data=array();
$sql=mysql_query("SELECT * FROM reftypes
WHERE Article_ID=1 ORDER BY ID ASC");
while($row = mysql_fetch_array($sql)){
$data[]=$row[2]; // i store in an array so that i can use later..
}
2:
foreach ($data as $ref) {
$counter=1;
switch ($ref) {
case "article":
$sqlarticle= mysql_query("SELECT Title
FROM book WHERE Article_ID=1 ORDER BY ID ASC");
echo mysql_result($sqlarticle, $counter); //i want to get only one out of book table
$counter++;
break;
...
...
But $sqlarticle does not seem to work.
I want to display as:
+-----------+----------+
|Article_ID |Reference |
+-----------+----------+
| 1 | article1 |
| 1 | book1 |
| 1 | article2 |
| 1 | article3 |
+-----------+----------+
I know it is a long question and for experts or experienced people it is very trivial, but that is where I'm stuck.
SELECT
*
FROM
reftypes R
WHERE
Article_ID=your_id
LEFT JOIN books B ON (B.Article_ID = R.Article_ID AND R.Ref_Types = 'book')
LEFT JOIN articles A ON (A.Article_ID = R.Article_ID AND R.Ref_Types = 'article')
ORDER BY
R.id ASC;
Even if the database is wrongly modeled, I think.
What about the followin model instead?
""although especially question owners should respect any kind of effort and input, -i am thankful- i can not understand why some people try to think of question's holder as well-informed or experienced as themselves, or worse comment from higher level. ""
anyway, my question was about to get values one by one, here is how i did it;
$data=array();
$sql=mysql_query("SELECT * FROM reftypes
WHERE Article_ID=1 ORDER BY ID ASC");
while($row = mysql_fetch_array($sql)){
$data[]=$row[2]; // i store in an array so that i can use later..
}
$articlecount=0;
$bookcount=0;
foreach ($data as $value) {
switch ($value) {
case "article":
$sqlarticle=mysql_query("SELECT RefArticleTitle
FROM ref_article
WHERE $article_ID=Article_ID
ORDER BY ID ASC");
$articles= mysql_result($sqlarticle, $articlecount);
echo $articles;
echo "\n";
$articlecount++;
break;
case "book":
$sqlbook=mysql_query("SELECT RefBookName
FROM ref_book
WHERE $article_ID=Article_ID
ORDER BY ID ASC");
$books= mysql_result($sqlbook, $bookcount);
echo $books;
echo "\n";
$bookcount++;
break;
...
...
as a result, i got what i required..
+-----------+----------+
|Article_ID |Reference |
+-----------+----------+
| 1 | article1 |
| 1 | book1 |
| 1 | article2 |
| 1 | article3 |
+-----------+----------+
thanks to whoever interested in the topic..
$result=mysqli_query("select ref_types from reference type");
while($row=mysqli_fetch_array($result))
{
$table=$row[0];
$result1=mysqli_query("select * from $table");
while($row1=mysqli_fetch_array($result1))
{
var_dump($row1);
}
}