Problem with making complex query in Eloquent - sql

I have to make a query in which I should determine if product (wine) belongs to either white wine or red wine type, but here is the catch - table wine has only variety_id and variety table has type_id(red or white). I suppose that I should do it with a subquery but I just can't figure out how with Eloquent methods. Three tables are connected this way: wine can have only one variety and one variety can have multiple wines, one variety can have only one type, while one type can have multiple varieties. How could I check if a wine belongs to either type as that's one of the filters (along with product label, availability and so forth)?
Edit: I tried this but unsuccessfully:
$wines->whereHas('variety.type',function($query){
$query->where('id','=',1);
})->get();
Explanation: I know that id of the red type in Type table is 1 and by this I wanted to get all the red wines

If you have the following relationsips:
In model Wine:
public function variety()
{
return $this->belongsTo(Variety::class);
}
and in model Variety:
public function type()
{
return $this->belongsTo(Type::class);
}
you can get what you need with:
$colors = ['red', 'white'];
$redAndWhiteWines = Wine::whereHas('variety.type', function ($query) use ($colors) {
$query->whereIn('name', $typeNames);
})->get();
This is considering you have in your types table column name that has the actual color of the wine.

Related

Expected Name found { ; tring to find objects with specific field value

I'm trying to get familiar with graphql. So I have an entity called Car in BE. And I have only Cars exposed.
Now I'm trying to find all the cars from Cars, where year(launch) is certain say 2001. It's actually a variable. Now I think the following query should work.
query GetCars($y: String!) {
cars({ year: $y }) {
id
year
}
}
But it gives me error saying, Expected Name found {, it throws the error at the second dollar sign.
filters and where is also undefined.
Can anyone give me some hint to resolve this problem?
Using: GraphiQL
In cars({ year: $y}) the name of your argument is missing - arguments always need to have a name in GraphQL and the name of your arguments and their GraphQL type are defined in your schema.
Assuming your cars fields has an argument called year in your schema, you need to rewrite your query to: cars(year: $y).
Assuming your cars fields has a filter or something like that defined in the schema and that filter expects an input object type that has - maybe among others - a field year, you would write: cars(filter: { year: $y }).

Compare two database fields in extbase repository

I am using TYPO3 8. In my extension I have a database table "company" in which I store for each company the total number of places (number_places) and the number of occupied places (occupied_places).
Now I want to limit the search to companies which have available places left.
In MySQL it would be like this:
SELECT * FROM company WHERE number_places > occupied_places;
How can I create this query in the extbase repository?
I tried to introduce the virtual property placesLeft in my model but it did not work.
I don't want to use a raw SQL statement as mentioned below, because I already have implemented a filter which uses plenty of different constraints.
Extbase query to compare two fields in same table
You can do it like this in your repository class, please note the comments inside the code:
class CompanyRepository extends \TYPO3\CMS\Extbase\Persistence\Repository
{
public function findWithAvailablePlaces(bool $returnRawQueryResult = false)
{
// Create a QueryBuilder instance
$queryBuilder = $this->objectManager->get(\TYPO3\CMS\Core\Database\ConnectionPool::class)
->getConnectionForTable('company')->createQueryBuilder();
// Create the query
$queryBuilder
->select('*')
->from('company')
->where(
// Note: this string concatenation is needed, because TYPO3's
// QueryBuilder always escapes the value in the ExpressionBuilder's
// methods (eq(), lt(), gt(), ...) and thus render it impossible to
// compare against an identifier.
$queryBuilder->quoteIdentifier('number_places')
. \TYPO3\CMS\Core\Database\Query\Expression\ExpressionBuilder::GT
. $queryBuilder->quoteIdentifier('occupied_places')
);
// Execute the query
$result = $queryBuilder->execute()->fetchAll();
// Note: this switch is not needed in fact. I just put it here, if you
// like to get the Company model objects instead of an array.
if ($returnRawQueryResult) {
$dataMapper = $this->objectManager->get(\TYPO3\CMS\Extbase\Persistence\Generic\Mapper\DataMapper::class);
return $dataMapper->map($this->objectType, $result);
}
return $result;
}
}
Notes:
If you have lots of records to deal with, I would - for performance reasons - not use the data mapping feature and work with arrays.
If you want to use the fluid pagination widget, be sure you don't and build your own pagination. Because of the way this works (extbase-internally), you'd get a huge system load overhead when the table grows. Better add the support for limited db queries to the repository method, for example:
class CompanyRepository extends \TYPO3\CMS\Extbase\Persistence\Repository
{
public function findWithAvailablePlaces(
int $limit = 10,
int $offset = 0,
bool $returnRawQueryResult = false
) {
// ...
$queryBuilder
->setMaxResults($limit)
->setFirstResult($offset);
$result = $queryBuilder->execute()->fetchAll();
// ...
}
}
I think you cant do this using the default Extbase Query methods like equals() and so on. You may use the function $query->statement() for your specific queries like this.
You also can use the QueryBuilder since TYPO3 8 which has functions to compare fields to each other:
https://docs.typo3.org/typo3cms/CoreApiReference/latest/ApiOverview/Database/QueryBuilder/Index.html#quoteidentifier-and-quoteidentifiers
It's fine to use this QueryBuilder inside Extbase repositories. After this you can use the DataMapper to map the query results to Extbase models.
In case of using "statement()" be aware of escaping every value which may cause any kind of SQL injections.
Based on the current architecture of TYPO3, the data structure is such that comparing of two tables or, mixing results from two tables ought to be done from within the controller, by injecting the two repositories. Optionally, you can construct a Domain Service that can work on the data from the two repositories from within the action itself, in the case of a routine. The service will also have to be injected.
Note:
If you have a foreign relation defined in your table configuration, the results of that foreign relation will show in your defined table repository. So, there's that too.

confusion over which relationship to use in models for these two tables

I have a user table that consists of these columns:
| id | username | password | email | pants_size_id | shirt_size_id |
pants_size_id and shirt_size_id are filled with foreign tables id keys where I store a list of sizes for pants and shirts in different country specific measures, example of pants_size table:
| id | UK_sizing | US_sizing | IT_sizing |
a single user will have only one pants and shirt size so the user table is filled with the ID of the corresponding rows in the size tables.
what kind of relationship does this imply between the user model and the pants and shirt sizing models?
Also how can I retrieve the data inside the foreign table column (example IT_sizing) when returning auth user return \Auth::user(); instead of the numeric size_id ?
In other words how can I retrieve say '32' (a pants size) instead of the pants_size_id (let's say '1').
Cato has the right answer, I can't exactly respond to it because of my rep but the logic in your other answer doesn't make sense from a relational standpoint.
Users don't belong to a size, instead, Users have a size.
To me it sounds like you mixed up the foreign and local key assignment it should be User->hasOne(pants_size).
In your model it would be the following, the explicitness of the keys isn't great, but if you have some weird thing laravel can't figure out this should work.
public function pants_size(){
return $this->hasOne('App\Pants_size','id','pants_size_id');
}
public function shirt_size(){
return $this->hasOne('App\Shirt_size','id','shirt_size_id');
}
To answer the other question of how to find the size (32), since you're dealing with three different measurements you have to have a where clause on the specific measurement the 32 represents, and get the id. If you specifically wanted the users you would call the eloquent query as so:
\Auth::User()->pants_size()->(..whatever measurement you want..)
Create a function establishing a hasOne relationship for both pants_size and shirt_size in the user model. Be sure to set the foreign key and local key correctly if you don't want Laravel to assume default keys (see here for details).
Once the functions are created, you will be able to obtain data about the user's size information like so: App\Model\User::find(123)->pants_size->UK_sizing. (This example is for a user with ID of 123).
this is how I made it work:
in USER model:
public function pants_size(){
return $this->belongsTo('App\Pants_size');
}
public function shirt_size(){
return $this->belongsTo('App\Shirt_size');
}
In Pants_size and Shirt_size Models:
public function user(){
return $this->hasMany('App\User');
}
That last one works also with hasOne.
The code I use to retrieve the data is:
public function index()
{
echo $user = User::find($id);
echo $pants = User::find($id)->pants_size->it_sizing;
echo $shirt = User::find($id)->shirt_size->it_sizing;
}

kohana ORM question

i am using kohana ORM in order to get some results from the database. My problem is: even though i have consulted the documentation, i can't find a way to select only the column i am interested in. To be more explicit, i have:
$sale_stock = Model::factory('product_type')
->where('product_type_id','=', $id )
-> find_all();
var dumping it, it selects me all the "SELECT product_type.* from product_type where etc".
But i want to select only the 'stock' field from the salestock table. doing find('stock') instead find_all() returns a weired object... Where am i wrong, and how can i actually select only the column 'stock' using kohana orm?
thank you!
ORM methods find() and find_all() always select all table columns, so there is two ways to get specified fields:
Load full table rows and get columns
from it:
$sale_stock = Model::factory('product_type')
->where('product_type_id','=', $id )
-> find_all();
// get array of id=>stock values
$columns = $sale_stock->as_array('id', 'stock');
Create special method in model using
Query Builder:
// model Model_Product_Type
public function get_stocks($product_type_id)
{
return DB::select(array('stock'))
->from($this->_table_name)
->where('product_type_id', '=', $product_type_id)
->execute($this->_db);
}
I realise this isn't exactly what you're looking for, but I've pulled the following from the Kohana documentation ...
$articles = ORM::factory('article')->select_list('id', 'title');
foreach ($articles as $id => $title)
{
// Display a list of links
echo html::anchor('articles/'.$id, $title);
}
// Display a dropdown list
echo form::dropdown('articles', $articles);
You could think of it as a discount, two fields for the price of one.
It's common practice for ORMs to return a 'non-standard' object when partial model or merged model fields are requested. This prevents confusing operations using the original object (ie. how do you save an object when it contains only 2 of 8 fields, plus maybe some fields from another model?).
If you print_r the object, and give me an indication of how that looks ... it might be just what you want.
I know this is an old question, but i found maybe easier solution:
$sale_stock = ORM::factory('product_type')
->where( 'product_type_id','=', $id )
->find_all();
die($sale_stock->stock);

Lucene Query Syntax

I'm trying to use Lucene to query a domain that has the following structure
Student 1-------* Attendance *---------1 Course
The data in the domain is summarised below
Course.name Attendance.mandatory Student.name
-------------------------------------------------
cooking N Bob
art Y Bob
If I execute the query "courseName:cooking AND mandatory:Y" it returns Bob, because Bob is attending the cooking course, and Bob is also attending a mandatory course. However, what I really want to query for is "students attending a mandatory cooking course", which in this case would return nobody.
Is it possible to formulate this as a Lucene query? I'm actually using Compass, rather than Lucene directly, so I can use either CompassQueryBuilder or Lucene's query language.
For the sake of completeness, the domain classes themselves are shown below. These classes are Grails domain classes, but I'm using the standard Compass annotations and Lucene query syntax.
#Searchable
class Student {
#SearchableProperty(accessor = 'property')
String name
static hasMany = [attendances: Attendance]
#SearchableId(accessor = 'property')
Long id
#SearchableComponent
Set<Attendance> getAttendances() {
return attendances
}
}
#Searchable(root = false)
class Attendance {
static belongsTo = [student: Student, course: Course]
#SearchableProperty(accessor = 'property')
String mandatory = "Y"
#SearchableId(accessor = 'property')
Long id
#SearchableComponent
Course getCourse() {
return course
}
}
#Searchable(root = false)
class Course {
#SearchableProperty(accessor = 'property', name = "courseName")
String name
#SearchableId(accessor = 'property')
Long id
}
What you are trying to do is sometimes known as "scoped search" or "xml search" - the ability to search based on a set of related sub-elements. Lucene does not support this natively but there are some tricks you can do to get it to work.
You can put all of the course data associated with a student in a single field. Then bump the term position by a fixed amount (like 100) between the terms for each course. You can then do a proximity search with phrase queries or span queries to force a match for attributes of a single course. This is how Solr supports multi-valued fields.
Another workaround is to add fake getter and index it
Something like:
#SearchableComponent
Course getCourseMandatory() {
return course + mandatory;
}
Try
+courseName:cooking +mandatory:Y
We use pretty similar queries and this works for us:
+ProdLineNum:1920b +HouseBrand:1
This selects everything in product line 1920b that is also a house brand (generic).
You can just create queries as text string and then parse that to get your query object. Presume you have seen Apache Lucene - Query Parser Syntax ?