cakephp 3 deeper associations - associations

I'm trying to find the right way to handel the next tabel setup.
Products is my main table and contains an id.
UsersCarts has an field product_id and with belongsTo it also loads the products.
So far no problem. I also have an table ProductsNettoDeals where I store possible netto prices. This table has it's own id an an product_id for the hasOne relation when loading products in the productcontroller.
When I load the UsersChart it loads it belongsTo products, but I also want the association with the table ProductsNettoDeals, but this table is connected with products through the id (products.id = products_netto_deals.product_id). I cant find the right syntax or association for the querybuilder. What I have so far:
// src/Model/Table/UsersCartsTable.php
namespace App\Model\Table;
use Cake\ORM\Table;
use Cake\ORM\Query;
use Cake\Validation\Validator;
class UsersCartsTable extends Table
{
public function initialize(array $config)
{
$this->addBehavior('Timestamp');
$this->belongsTo('Products');
$this->belongsTo('ProductsNettoDeals');
}
public function validationDefault(Validator $validator)
{
return $validator
->notEmpty('user_id', 'A user id is required')
->notEmpty('product_id', 'A product id is required');
}
public function findCart(Query $query, array $options)
{
$query
->where([
'user_id' => $options['user_id'],
//'active' => '1'
])
->contain(['Products','ProductsNettoDeals'])
//->select(['product_id','created','products.name','products.price','products.delivery_time']);
;
return $query;
}
}
In this setup I always have the problem that the matching of the id's are wrong. With the products it's good. The users_carts.product_id links with the products.id, but the products_netto_deals.product_id should link with product.id and not with the users_carts.id. I tryed foreign key and bind, but both are an part of an solution.
Beside this problem, when connecting the association ProductsNettoDeals I get an error when activating the commented active => '1'. This field indicates if an record is active and should be loaded. The same field exist in the products_netto_deals tabel to indicate if an netto price is active.
Error: SQLSTATE[23000]: Integrity constraint violation: 1052 Column 'active' in where clause is ambiguous
Can anyone point me in the right direction for the correct way of the association and the error warning. Thanks in forward.

CakePHP uses joins for fetching associations, in order to speed ud the information retrieval process. This means that when having column names repeated across different table, you will need to prefix the column names in the query clause"
Instead of:
$query->where(['active' => true]);
Do:
$query->where(['UsersCarts.active' => true])

Related

How to retrieve only the relation ID, not the whole entity in MikroORM?

I have the following basic test entities:
#Entity()
class Author {
#PrimaryKey()
public id!: number;
#Property()
public name!: string;
}
#Entity()
class Book {
#PrimaryKey()
public id!: number;
#Property()
public title!: string;
#ManyToOne({joinColumn: 'authorID'})
public author!: Author;
}
What i'm trying, is to select only a single Book record, with its 'author', but I care only about its ID, I don't want to actually load the entity.
If I simply call this, it won't work (no author data loaded at all):
em.getRepository(Book).findOne({id: 1}, {fields: ['id', 'title', 'author.id']});
'author.id' doesn't do the trick, the SQL doesn't even contain the 'authorID' field.
If I add 'author' to the fields list as well, it works, author is loaded (only with the ID), but as a separate entity, with a separate, additional SQL statement! That's what I'm trying to avoid.
em.getRepository(Book).findOne({id: 1}, {fields: ['id', 'title', 'author', 'author.id']})
#1. SQL
select `b0`.`id`, `b0`.`title`, `b0`.`authorID` from `book` as `b0` where `b0`.`id` = 1 limit 1
#2. SQL (this wouldn't be neccessary as I want only the ID)
select `a0`.`id` from `author` as `a0` where `a0`.`id` in (2)
--> Result:
Book: { id: 1, title: 'a book', author: { id: 2 } }
The only way I found is to add the specific 'authorID' field too to the Book entity:
#Property()
public authorID!: number;
But, I'd like to avoid introducing these foreign key columns, it would be better to handle through the already existing and used 'author' relation (only by the 'id' property).
Does any solution exists where I could retrieve a relation's ID without generating a 2nd SELECT statement (for the relation), and even avoid introducing the foreign key (next to the already existing relation property)? Would be great to receive through the relation without any extra sql statement.
Thanks in advance.
It is correct behaviour you see the second query, that is how population works, and the fact that you want just a single property from the entity does not change anything, you still populate the relation, and each relation will use its own query to load it. You can use LoadStrategy.JOINED if you want to use a single query. But that would still do a join for that relation, which is not needed for your use case.
Given you only want the FK to be present, you dont need to care about the target entity at all. This should do the trick too:
em.getRepository(Book).findOne(1, {
fields: ['id', 'title', 'author'],
populate: [],
});
This way you say you want those 3 properties to be part of what's selected from the Book entity. You already have the author property, which represents the FK. You will end up with what you want once you serialize such entity. During runtime, you will see entity reference there - an entity with just the PK. It is represented as Ref<Author> when you console.log such entity.
Note that you need that populate: [] there, as otherwise it would be inferred from your fields which contains author property, and that would trigger the full load of it.

Getting data from the database based on dropdown selection in Symfony forms

I have two Entities, First one is the Company Entity and the other one is Events Events Entity.
ManyToOne(targetEntity="App\Entity\Company", inversedBy="events"
On the events from I have created a drop-down doing something like following.
->add ( 'company' , EntityType::class , [
'class' => Company::class ,
'choice_label' => function ( Company $company ) {
return $company->getName ();
}
is there any way I can obtain other values from the database for the selected company. In the database, there is a column called app_client_id which I want to get on dropdown selection so I can use that to call external API'S.
By default, in the dropdown, you get the result of the __toSring() function in the entity. Consider setting there what you want to read when selecting.

Multi row aggregation select result includes whole object hydrated as well

On Symfony2 I have an entity called Product which is managed by Doctrine2 and being related with a single category and location.
Class Product {
protected $id;
protected $title;
// n additional fields
protected $productCategory;
protected $productDepot;
}
What I need to achieve is, select category and location COUNT's in a single query.
So I'm executing the following COUNT select with some groupings on the following DQL.
$this->em->getRepository('MyCoreBundle:Product')
->createQueryBuilder('P')
->addSelect('IDENTITY(P.productCategory) AS category_id')
->addSelect('IDENTITY(P.productDepot) AS location_id')
->addSelect("COUNT(P.id) AS tag_count")
->addGroupBy('P.productCategory')
->addGroupBy('P.productDepot')
->getQuery()
->getScalarResult();
However when it is executed, the whole result is as follows:
$result = [
0 => [
P_id => ...,
P_title => ...,
P_stock => ....,
/*
* Many many additional fields of the entity
*/
category_id => 15,
location_id => 40,
tag_count => 20
],
...
];
However obviously I just need category_id, location_id and tag_count fields. Since the table is a huge one (like everyone has one, nowadays), I'm trying to minimize footprint of the query as much as possible.
What I tried so far is adding a partial field of id (as follows) however it didn't make sense.
$this->em->getRepository('MyCoreBundle:Product')
->createQueryBuilder('P')
->addSelect('partial P.{id}')
->addSelect('IDENTITY(P.productCategory) AS category_id')
->addSelect('IDENTITY(P.productDepot) AS location_id')
->addSelect("COUNT(P.id) AS tag_count")
->addGroupBy('P.productCategory')
->addGroupBy('P.productDepot')
->getQuery()
->getScalarResult();
Am I trying to achieve something impossible, or do you have any idea to get just the aggregation fields (or with just an additonal ID field) of an an entity?

One to one relationship in Silverstripe

I'll try and put this as simply as possible but basically what I am trying to achieve is this.
There are two page types with a one to one relationship, car and owner. I want to be able te be able to select an owner through a dropdown on the car page. If an owner is already linked to another car I don't want it to appear in the dropdown.
I know that I'll need an if statement but I I'm finding it hard to puzzle out how it should go. I followed this tutorial to create the dropdown and it worked quite well.
Thanks in advance.
You can modify the function that gives you the dropdown values. Your DataObject::get() call
can have a filter for the second argument. Simply select all owners that have a CarID of 0.
So, from the tutorial you provided, you can use this modified code:
new DropdownField(
'OwnerID',
'Please choose an owner',
Dataobject::get("Owner","CarID='0'")->map("ID", "Title", "Please Select")
);
2 things to note:
This assumes your DataObjects are called Car and Owner (change as necessary, but keep the ID at the end of the name as it is written above)
This may not work depending how you set up the relationships with the $has_one assignments on your DataObjects. If there is no CarID field on the Owner table, then this code won't help you (you may have it set up vice-versa). In that case, you'll have to create a function that loops through all cars, and then removes the DataObjects from that DataObjectSet that have an OwnerID of 0. Add a comment if this isn't making sense.
Benjamin Smith' answer is perfectly valid for the dropdown you were asking for, just wanted to point to another approach: instead of taking care of the one-to-one relation yourself, there's the 'HasOneComplexTableField' handling this for you.
use the following code for your Car class:
class Car extends Page {
public static $has_one = array(
'Owner' => 'Owner'
);
function getCMSFields() {
$fields = parent::getCMSFields();
$tablefield = new HasOneComplexTableField(
$this,
'Owner',
'Owner',
array(
'Title' => 'Title'
)
);
$tablefield->setParentClass('Car');
$tablefield->setOneToOne();
$tablefield->setPermissions(array());
$fields->addFieldToTab('Root.Content.Owner', $tablefield);
return $fields;
}
}
note the 'setOneToOne()' call, telling the tablefield to only let you select Owners which aren't already selected on another car.
you'll find more information on this in the silverstripe tutorial: http://doc.silverstripe.org/framework/en/tutorials/5-dataobject-relationship-management

how to customise search results of apachesolr (drupal 6)

can somebody help me how to customize the search result of a apache solr search. i was only able to access these variables [comment_count] => [created] => [id] => [name] => [nid] => [title] => [type] => [uid] => [url] => [score] => [body].
how can i access other variable like status, vote .... from the index ( i don't want to access the database for retrieving these values, i want to get it from the index itself)
i need to display no of votes for that specific node in the result snippet
i need to understand
1. how to index votes field
2. how to show the vote, status... in result snippet.
Votes are a poor choice for indexing for a couple of reasons:
Votes can change quickly
When a vote is made, the node is not updated. As such, apachesolr won't know to re-index the node to pick up the change.
If by 'status' you mean the node->status value, then the answer is that it will always be 1. Unpublished nodes are never indexed.
Now, if you want to add something else to the index, you want hook_apachesolr_update_index(&$document, $node) - this hook gets called as each node is being indexed, and you can add fields to $document from $node to get the values into the solr index. However, you want to use the pre-defined field prefixes - look at schema.xml to find the list.
Below is example of code to add fields for sorting, and for output.
/**
* Implementation of hook_apachesolr_update_index()
* Here we're adding custom fields to index, so that they available for sorting. To make this work, it's required to re-index content.
*/
function somemodule_apachesolr_update_index(&$document, $node) {
if ($node->type == 'product') {
$document->addField('sm_default_qty', $node->default_qty);
$document->addField('sm_sell_price', $node->sell_price);
$document->addField('sm_model', $node->model);
foreach ($node->field_images AS $image) {
//$imagecached_filepath = imagecache_create_path('product', $image['filepath']);
$document->addField('sm_field_images', $image['filepath']);
}
}
}
/**
* Implementation of hook_apachesolr_modify_query()
* Here we point what additional fields we need to get from solr
*/
function somemodule_apachesolr_modify_query(&$query, &$params, $caller) {
$params['fl'] .= ',sm_default_qty,sm_field_images,sm_sell_price,sm_model';
}
If you want to totally customize output, you should do following:
1) Copy search-results.tpl.php and search-result.tpl.php from /modules/search to your theme's folder.
2) Use the $result object as needed within search-result.tpl.php
3) Don't forget to clear the theme registry by visiting admin/build/themes
Or as mentioned about, you can override using preprocessor hooks.
Regards, Slava
Another option is to create a view(s) of your liking with input argument nid, then create the following preprocess in your template.php file:
function MYTHEME_preprocess_search_result(&$vars) {
$vars['myView'] = views_embed_view('myView', 'default', $vars['result']['node']->nid);
}
Matching the view name 'myView' with the variable name makes sense to me. Then you can use the variable $myView in your search-results.tpl.php file.
Here's a video by the makers of the Solr Search Integration module with an overview on how to customise what nodes and fields are indexed, and what Solr spits out as a search result...
For Drupal 6:
http://sf2010.drupal.org/conference/sessions/apache-solr-search-mastery.html
And Drupal 7:
http://www.acquia.com/resources/acquia-tv/conference/apache-solr-search-mastery
It all looks very customisable!

Resources