How to match data from two different database tables and display it - php

I am looking for a solution which would withdraw essential data used to match other data from a different table. Then I would like to display this data inside a blade in a form of a table.
Inside the database, I have a "matching" entity which stores user's credentials which I would like to use for matching (for example desired price of the product). Entity contains "peopleID" as "matching" belongs to website users. When user is created, we assign matching options which are getting stored inside that "matching" entity. The number of rows inside Matching entity depends on the number of counties chosen during user creation stage.
I know that to withdraw matching data from the database I need to use a foreach loop.
The problem I have is when I output data inside the blade. For some reason it matches products only with the last item from an array. It should match prodtucts with all matching credentials.
Code:
$matchings = Match::where('PeopleID', '=', $id)->get();
$sales = DB::table('sales');
foreach ($matchings as $matching)
{
$county = $matching->county;
$sales->where('county', '=', $county);
}
$results = $sales->get();
So for one of the customers I have two matchings with different "counties". It only displays data for the last one added. How could I make it display data for other matching which contains a different county. I hope you know what I mean.
Thanks for any help.
Update - Major part of the code is done. Thank you for your help.
The second question is about adding the rest of matching options. As stated before the number of matches depends on the number of counties added. Each match has its own attributes. The idea is to show matched results for each county.
I know I will need some if statements to do this.
Here is an example which I would like to implement:
$maxprice = $match->maxprice;
$minprice = $match->minprice;
$brand_a = $match->brand_a;
$brand_b = $match->brand_b;
if($maxprice != '0')
{
$sales = DB::table('sales')->where('maxprice', '<=', $maxprice);
}
if($minprice != '0')
{
$sales = DB::table('sales')->where('minprice', '>=', $minprice);
}
if($brand_a == '1')
{
$sales = DB::table('sales')->where('brand_a', '1');
}
if($brand_b == '1')
{
$sales = DB::table('sales')->where('brand_b', '1');
}
To this code:
$user = User::find($id); // get our User with the Id (person id?)
$matches = $user->matches; // get all the matches
// or you could one line the above: $matches = User::find($id)->matches;
// get all the counties in the returned matches, could use pluck() method in higher version of laravel
$counties = [];
foreach($matches as $match) {
$counties[] = $match->county;
}
$results = DB::table('sales')->whereIn('county', $counties)->get();
Many Thanks for your help!
#update
Relationships:
Match:
public function people()
{
return $this->belongsTo('People', 'PeopleID', 'PeopleID');
}
People:
public function matches()
{
return $this->hasMany('Match', 'PeopleID', 'PeopleID');
}
I have a connection between those as Match holds people's "search" credentials. The first solution which you have provided works perfectly. Now, this solution filtered out sales by county which is a good move as now they need to be filtered by minimum and maximum price (minprice, maxprice) and other credentials such as brand_a and brand_b.
The idea of brand_a and brand_b:
Checkboxes are responsible for changing brand_a and brand_b value inside Matching. If these are checked the values inside Matching entity become '1'. If these are not checked they become '0' which means that sales don't have to be filtered out by those values.
Sales entity contains "Holdings" attribute. The value of "Holdings" can be brand_a or brand_b. Sales also contains "Price".
So, to make this clear:
Sale Entity contains: SaleID, Price, Holdings, County.
Holdings are values: brand_a or brand_b.
Price is just a number.
County is plain text.
Matching Entity contains: PeopleID, MinimumPrice, MaximumPrice, brand_a, brand_b, county.
PeopleID is a foreign key. We need to know which matching belongs to what user.
(there can be multiple matchings for one user depending on the number of counties chosen).
MinimumPrice and MaximumPrice are numbers.
brand_a and brand_b are the values (1 or 0) depending if the checkboxes were checked.
County is the name of a county.
Now, if person 1543 (peopleID = 1543) contains 3 matchings, each containing different search credentials.
1st:
PeopleID: 1543
MinimumPrice: 1000
MaximumPrice: 7000
brand_a: 0
brand_b: 1
county: county_a
2nd:
PeopleID: 1543
MinimumPrice: 2500
MaximumPrice: 10000
brand_a: 1
brand_b: 1
county: county_f
3rd:
PeopleID: 1543
MiniumPrice: 2000
MaximumPrice:9500
brand_a: 0
brand_b: 0
county: county_d
I need to match this data against the data that is inside the Sales. There can be over a 1,000 different sales with different prices etc. I just need to filter them and display Sales that are desired by the person based on person's matching.
I hope this better presents you the situation. Thanks.

In short, I belive you need to leverage Eloquent Relationships to easily retrieve the data you desire. Read up on relationships in the docs here: https://laravel.com/docs/4.2/eloquent#relationships.
I've made some assumptions so you may need to work the following into your actual setup. Also, I found it quite difficult to 100% understand your DB structure from your question, but from what I gather from your question your DB structure is like this:
User/Person *has many* Match
(Note: Name may be wrong, but you didn't mention what it's called in the question all I can see is the word "user" and "personId")
Match *belongs to* User/Person
Based on this I think you should set up your relationships like this:
User
class User extends Eloquent {
public function matches()
{
return $this->hasMany('Match');
}
//...
}
Match
class Match extends Eloquent {
public function user()
{
return $this->belongsTo('User');
}
//...
}
Then your code can look like this:
$user = User::find($id); // get our User with the Id (person id?)
$matches = $user->matches; // get all the matches
// or you could one line the above: $matches = User::find($id)->matches;
// get all the counties in the returned matches, could use pluck() method in higher version of laravel
$counties = [];
foreach($matches as $match) {
$counties[] = $match->county;
}
$results = DB::table('sales')->whereIn('county', $counties)->get();
A better approach to this issue (I think) would be to give County it's own entity then Match would have a county_id then you can use a has many through relationship if you can link up Match, County and Sales. You can read more about has many through in the docs here: https://laravel.com/docs/4.2/eloquent#has-many-through
Also, a side point... this part of your code:
$sales->where('county', '=', $county);
will just continuously add where statements to your query which I would imagine won't return anything if there's more than one.
Just to make this clearer, imagine you have 2 counties "county_1" and county "county_2", through your for loop your query would end up like this:
WHERE COUNTY = "county_1" // first loop
AND COUNTY = "county_2" // second loop
and as you can see a match cannot be two counties at one time! So you were probably looking for ->orWhere('county', 'county_value') after the first one was added, but a better approach is to use whereIn('county', $countiesArray) which you can pass an array you've built up, which is what I've done above.
Hope this helps! Let me know if it wasn't clear.
Edit
The best approach would be to establish relationships between the Sale and Match entities. As I still don't fully understand your database schema I can't advise so well on how you would approach that. If you gave some more details it may be possible.
Alternatively, you could approach the code by building up an array which you will use for applying conditions to your query. Consider your updated question with the four if statements, anytime you're repeating yourself like that, more often that not it can be simplified.
$filters = [
'maxprice' => $match->maxprice,
'minprice' => $match->minprice,
'brand_a' => $match->brand_a,
'brand_b' => $match->brand_b,
];
$salesQuery = DB::table('sales');
foreach($filters as $key => $value) {
if($value) {
$salesQuery->where($key, $value);
}
}
$results = $salesQuery->get();
As your conditionals are a bit stricter in your code from your question, you do to it like this instead:
foreach($filters as $key => $value) {
if ($value) {
if (in_array(['brand_a', 'brand_b'], $key)) {
$salesQuery->where($key, $value);
} else if($key === 'minprice') {
$salesQuery->where($key, '>=' $value);
} else if($key === 'maxprice') {
$salesQuery->where($key, '<=' $value);
}
}
}
the good thing about this approach is that you can easily add new conditionals via the filters array without having to write a new if statement and query/where code each time.
I'll stress this probably isn't the best approach, ideally you'd leverage Eloquent Relationships, but it may be a starting point if you can't figure that out right away.

Related

How to get an array of country names by an array of their id's for use in country verification

I am sure there is a similar question, but I am unsure what to search to find my answer (If you know what I am looking for, please just comment with the link or whatever you do). I need a way to pull all the countries allowed on a particular page (Just the name column in DB) and put it in an array. example:(United States, Canada, New Zealand, etc..) but in the database I have it set up when the pages are created to insert in the DB an array of the id #'s (id column in countries table) works perfectly fine inserts as (1,22,30, etc) 0 is not a countries table row, but I use 0 as the country ID for All Countries. I am just unsure where to start or how to "foreach" this. Here is a little bit of what I am trying to achieve in my Laravel controller:
public function view(Page $page, Request $request)
{
// $page->countries = array of country id's (1,12,14,etc)
// How do i retrieve the country names
$ip = $request->ip();
$geo_info = \Location::get($ip);
$geo_country = $geo_info->country;
$countries = Country = ??
// How do I add the country names to an array (United States, Mexico, etc)
// instead of the country id's while keeping the id's so its smaller data in the column for the page countries
if(in_array($geo_country, $countries)) {
return view();
} else {
return 'Country not allowed.';
}
}
How exactly would I format to pull the countries by the IDs and then attach only the country's names column together in an array with ", "?
Sorry if this post is confusing, I am honestly confused myself.
Assuming you have a Country model and corresponding table with country ID and country name, I think you are looking for a simple WHERE IN query, eg:
// $page->countries = string list of country ids "1,2,3 ..."
// Let's convert that to an array and use is in our query
$allowed_countries = explode(',', $page->countries);
$countries = Country::whereIn('id', $allowed_countries)->get();
That should give you a Collection of your allowed countries. Now to check if $geo_country is in that collection (assuming the country name is in a field called name), you can use the contains() Collection method:
if ($countries->pluck('name')->contains($geo_country)) {
return view();
} else {
return 'Country not allowed.';
}
Could probably do something like this. Just create a empty array, loop then set the persisting data to that array. Hopefully this gives you an idea.
$countries = array();
foreach($geo_country as $countries) {
$countries[] = $geo_country;
}

Laravel mySQL view return by specific ID

So I am quite new to Laravel, and I have a situation, where I am trying to gather data from a pivot table (contains 2 foreign keys only) in order to retrieve data from other tables.
Before everything, I'd like to note, that word "campaign" is the same as "box". Simply it differs in database and front.
I have multiple boxes, that contains specific gifts.
I have set the URL of the box to be something as such: http://127.0.0.1:8000/box/1
http://127.0.0.1:8000/box/2
etc..
I have done so, by simply using a button with the {id}:
View the box
My plan is, to print out only that specific boxes gifts (right now, all boxes print out all gifts).
I have tried to use the ->where option within my function, although, it seems that I can't try equaling to the campaigns ID.
Incorrect code:
function box(){
$data = array(
'list'=>DB::table('campaigns_gifts')
->join('gift_items', 'gift_items.id', '=', 'campaigns_gifts.gift_foreignK')
->select('gift_items.*')
->where($campaign_foreignK = '{id}')
->get()
);
return view('DBqueries.boxView', $data);
}
My question is, how can I specifically return data, that only belongs to that specific box, since I am not able to use mysql where option.
For reference, these are the database tables:
Basically, I would need to match my URL's id with campaign_foreignK
Thank you in advance.
First of all, yout need to start to use Laravel Eloquent Models.
But doing by your way (the hardest):
You need to create a route in web or api, something like that:
Route::get('/box/{id}', [BoxController::class, 'view']);
Then you need to put this function on your controller:
function view($id){
/**
* You can do it by 2 ways:
* 1 - Do a where in the result of DB query (the bad way)
*/
$list = DB::table('campaigns_gifts')
->join('gift_items', 'gift_items.id', '=', 'campaigns_gifts.gift_foreignK')
->select('gift_items.*')
->where($campaign_foreignK = '{id}')
->get();
$list = (array)collect($list)->where('abc', 123);
/**
* Or the second way (the best is to use the Eloquent, but using DB the following is the best)
* 1 - Get the relations:
* Is git_items id the key for gift_foreignK ? i'm supposing that is it! so....
*/
$giftsIds = array_values((array)DB::select("select * from campaigns_gifts where campaign_foreignK = $id"));
$giftsIdsString = implode($giftsIds, ',');
$list = (array)DB::select("select * from gift_items where id in ($giftsIdsString)");
return view('DBqueries.boxView', ['list' => $list]);
}

How to count specific objects in an array collection based on a getter that return a string

I have an array of student objects. Each student has subjects as an array collection. Each subject has a function getStatus() that is calculated based on different arguments etc, thus there is no real property for status in the subject entity.
How can I count the number of subjects that are completed, in progress, and pending and display it per student in a table?
I could retrieve my students in my controller like this:
$students = $em->getRepository(Student::class)->findAll();
and then perhaps with loops count it somehow, but I don't get how.
I thought of creating a function that implements the filter on the array collection like seen in this answer, but I don't understand how to use that to filter on getStatus().
I also thought to implement ->matching with a criteria like this:
public function getSubjectsByStatus(string $status): ?Collection
{
$subjects = $this->subjects->matching(
Criteria::create()->where(Criteria::expr()->eq($this->getStatus(), $status))
);
return $subjects ?? null;
}
and then do a count on the returned collection, but the first parameter of eq() should be a string and I don't have a status property in the subject entity that can be used as a string, and adding a property now is not a good idea.
How can I count all subjects, pending subjects, completed subjects, and in progess subjects the best way?
You probably should consider making your status value an actual field in your database, since your problem would be easy to solve with a SQL/DQL query.
Without that being the case, here is how you could implement your getSubjectsByStatus method:
public function getSubjectsByStatus(string $status): ?Collection
{
return $this->tl1Configs->filter(function ($element) use ($status) {
return $element->getStatus() == $status;
});
}
But if you call that method three times to just count the amount of all status values, you are looping over your collection three times as well.
A "better" solution would probably be to make a specialized method to explicitly get these counts. This is just one way of achieving what you want though. A method to return an array of sub-collections instead of just status counts could is another solution if you want to actual work with the sub-collections (all depends on your actual usecase).
public function getSubjectStatusCounts(): array
{
$statusCounts = [];
foreach ($this->tl1Configs as $subject) {
$statusCounts[$subject->getStatus()] = ($statusCounts[$subject->getStatus()] ?? 0) + 1;
}
return $statusCounts;
}

php/mysql search table with unknown number of filters

I've seen on many shopping websites they have search filters on the side, and you can add any number of filters and it researches the data showing only the data that matches all of the filter queries.
As an example, if you go to ebay and look for a computer, you can filter by various specs of the computer to narrow the results.
The problem I'm having is working out how to do this for a table that has so many fields that a user may search by.
I have a table of Properties that I want to search by any number of parameters e.g. rent, location, etc.
I could create search queries for each possible option e.g. searchByAddress($array), searchByAddressAndRent($array), etc. but that's clearly not feasible.
Another way I could create separate queries for each field, then trigger separate search queries for each parameter i.e. searchByRent($array), searchByAddress($array) and allow the PHP application to compute which fields are common in all resulting arrays using array_intersect.
But I was wondering, there must be a proper technique in achieving this. This question is a bit long-winded and I couldn't find any tutorials on it from googling.
So my question is, what's the "right" method/technique of searching a database table with various search-filters?
If you create a class representing the property table, you can define a static array in the class detailing each field name and corresponding data type.
On the front end, each search field should correspond to the column name in the database, so the $_REQUEST array keys will match the column names.
After this, iterate through your search array, checking each variable exists in your class field definition array, and add it onto the search query.
Below is a very simplified example class which will hopefully give you the idea.
class Property () {
// search parameters come from $values_array
// each search field should correspond to the $field_definitions key
public function search($values_array = null) {
// populate the values array. Using this method meads you can pass an array directly
// into the search function, or you can rely on the $_REQUEST
$values_array = self::getValuesArray($values_array);
// setup the initial query
$query = "SELECT * FROM properties";
// set our initial join string
$join = "WHERE";
// loop each of our search values
foreach ($values_array as $field=>$value) {
// check the search key exists in our table definition and it's not empty
if (array_key_exists($field_definitions, self::$fields) && !empty($value)) {
// switch the datatype for the field so we can set the appropriate query string
switch (self::$field_definitions[$field]) {
case 'int':
$query .= "$join $field = {$value} ";
$join = "AND";
break;
default:
$query .= "$join $field = '%{$value}%' ";
$join = "AND";
break;
}
}
}
// now execute the query...
$results = mysql_query($query);
// do something to process the results and then return
return $results;
}
// basic function to grab our values from $_REQUEST if passed an empty array
private function getValuesArray($values_array = null) {
$values_array = (!empty($values_array)) ? $values_array : $_REQUEST;
return $values_array;
}
// variable containing all available fields in table and corresponding datatype
public static $field_definitions =
array( 'number'=>'int',
'street'=>'string',
'locality'=>'string',
'townland'=>'string',
'town'=>'string',
'postcode'=>'string'
);
}

Is there a way to search all the tables in a mySQL database?

Basically I have a database full of tables and I want to go through them all until I find a result that matches my search query. Is there a way to do this? Or at least a command to return all of the table names so that I could loop through them until I find the right value?
thanks!
Aaah, search engines. Exciting subject, but I would rather build something with internal intelligence rather than using brute force solution. Yes - checking every table/column in database is brute force and may result in sluggishness and false positives.
Let me present you with something I would use instead. With below solution each table/column worth scanning needs to be added manually, but everything else is automatic. Here's the usage:
$e = new SearchEngine();
$e->addTable('users', 'id', 'login'); // table, primary key name, column to be searched in
$e->addTable('users', 'id', 'last_name');
$e->addTable('towns', 'id', 'name');
print_r($e->search('austin')); // we search for exact match for word "austin"
And here's how it was implemented:
class SearchEngine {
protected $tables = array();
public function addTable($table, $key, $column) {
$this->tables[] = array(
'table' => $table,
'key' => $key,
'column' => $column
);
}
public function search($term) {
$q = array();
foreach ($this->tables as $t) {
list($table, $key, $column) = $t;
$q[] = "
SELECT
$key AS searched_key,
'$key' AS searched_key_name,
'$table' AS searched_table,
'$column' AS searched_column,
$column AS searched_value
FROM $table
WHERE $column = $term
";
}
$sql = implode(' UNION ', $q);
// query the database
// return results
}
} // class SearchEngine
Let's analyse example output:
searched_key | searched_key_name | searched_table | searched_column | searched_value
-------------+-------------------+----------------+-----------------+---------------
276 | id | users | login | austin
1782 | id | users | last_name | austin
71 | id | towns | name | austin
From the table above you can figure out that phrase "austin" was found in table users, column login (primary key 276) and column last_name (primary key 1782). It was also found in table towns in column name (primary key 71);
Such search result may be sufficient for you. Or else, you can further process the list to select full row from each table:
$out = array();
foreach ($rows as $row) {
$sql = "
SELECT * FROM {$row['searched_table']}
WHERE {$row['searched_key_name']} = {$row['searched_key']}
LIMIT 1
";
// query the database
// append result to $out array
}
return $out;
This way you will end up with full search result (as opposed to intermediate results from previous table):
id: 276, login: austin, last_name: Powers, email: austin.powers#gmail.com
id: 1782, login: michael, last_name: austin, email: michael.e#gmail.com
id: 71, name: austin, state: texas, country: usa
Because current implementation is restricted to fixed comparison operator (WHERE field = value), you may want to introduce some flexibility here. If so, search operator needs to be delegated to external class and injected into search() function:
public function search(SearchOperator $operator, $term) {
...
Then SearchOperator needs to be taken into account by replacing WHERE condition with the below:
WHERE {$operator->toSQL($column, $term)}
Now let's focus on SearchOperator implementation. Since operator implementation provides only one method, namely toSQL, we don't need full class, or even abstract class. Interface will suffice in this case:
interface SearchOperator {
public function toSQL($column, $term);
} // interface SearchOperator
And let's define couple of implementations representing = (equals) and LIKE operators:
class Equals implements SearchOperator {
public function toSQL($column, $term) {
return "$column = '$term'";
}
} // class Equals
class Like implements SearchOperator {
public function toSQL($column, $term) {
return "$column LIKE '$term'";
}
} // class Like
Naturally, any other implementation is possible - think about classes called StartsWith, EndsWith, or DoesNotContain.
See updated solution usage:
$e = new SearchEngine();
$e->addTable('users', 'id', 'login');
$e->addTable('users', 'id', 'last_name');
$e->addTable('towns', 'id', 'name');
print_r($e->search(new Like(), 'austin%')); // here we search for columns being LIKE 'austin%'
Time to leave some final remarks:
Above examples are incomplete. Database querying code was omitted for clarity.
SQL used in examples does not sanitize input data. I strongly urge you to use prepared statements with bound parameters to avoid huge security risk.
Search algorithm presented above is a naive one. Some optimisation can be done (i.e. grouping queries referring to the same table). But don't optimise prematurely - wait until it becomes a real issue.
Hoping this was helpful.
Very bad idea. However, should you need to search all tables (and you are using MySQL) you can get a list with:
SHOW TABLES;
And then loop through each and (assuming you know the columns) query them.
I guess you could use Mysql's full-text search for this?
As others have said, it's possible to extract all the tables names and their column names from meta data dictionary. Take a look at the "information_schema" database. You can get a list of tables which you can then iterate over.
But chances are you are using the database wrong. We don't query the database, we query the data model. The data model is implemented as a set of tables/views etc.
Can you provide us with some background as to why you need to do this? Maybe there are better alternatives?
You can get all the tables in your database with
SHOW TABLES;
Then you can loop through the tables and find out the structure for each table with
DISPLAY table_name;
But you still would need to have a vague idea about how to query to columns of each table in order to find anything. So unless you have a more specialized problem, e.g. al tables have the same known structure, I would agree with the sentiment that you might be using the database wrong and there might be a better way.
There is a nice library for reading all tables, ridona

Categories