Search API Solr integration with fivestar (or similar) rating system (fascet and sort) - drupal-7

I'm attempting to sort nodes by ratings using the Search API faceted search with Solr integration. I've already set up fivestar ratings (about 9 per node, its a large multi-axis rating system.) but i'm unable to index these ratings!
Can someone help me understand how to change this so I can use a facet search for ratings?
Otherwise, are there any recommendations on other modules (aside from fivestar) which would allow the votes to be indexed?
Thank you!
Justin

first you need install facetapi module -that's for facets.
second, on hook_update_index, you need to add rating to apachesolr index
<?php function module_apachesolr_update_index(&$document, $node) {
//add additional offers;
if (count($node->field_add_offers)) {
$field = $node->field_add_offers;
foreach ($field as $lang => $values) {
foreach ($values as $value) {
if (isset($value['value'])) {
$document->setMultiValue('sm_offers', $value['value']);
}
}
}
}
} ?>
Please note, it's just an example. I run 2 loops because of multilingual site and problem with this "und" key in field array. Here also you can not add all ratings, but calculate, for instance,one modifier per node, which will be used for sorting (if you don't have that one in ratings)
Third, add facets with using hook_facetapi_facet_info
<?php function module_facetapi_facet_info(array $searcher_info) {
return array(
'sm_games_facet' => array(
'name' => 'sm_games_facet',
'label' => t('games'),
'description' => t('Filter games'),
'field' => 'sm_games',
'field alias' => 'game',
'query type' => 'term',
'default widget' => 'facetapi_links',
'allowed operators' => array(FACETAPI_OPERATOR_OR => TRUE, FACETAPI_OPERATOR_AND => TRUE),
'default sorts' => array(
array('display', SORT_ASC),
),
)
);
} ?>
more about facets you can find at facetapi.api.php file;
Forth - reindex content and enable facet in apachesolr settings.
Regards, Slava

Related

CakePHP 3.x - Save many-to-many data

I am attempting to patchEntity with a join table, but I am unable to get it to save the associated records, and I think I have the backend code correct, but I am unsure of what the frontend code should look like...
Here is the scenario, I have three tables
Ledgers Table
id int
title string
Tribes Table
id int
name string
LedgersTribes Table
id int
ledger_id int
tribe_id int
Here is my backend code
public function ledgerSave($id = null)
{
$this->loadModel('Ledgers');
$ledger = $this->Ledgers->get($id, [
'contain' => ['LedgersTribes']
]);
if ($this->request->is(['patch', 'post', 'put'])) {
$ledger = $this->Ledgers->patchEntity($ledger, $this->request->getData(), [
'associated' => ['LedgersTribes']
]);
if ($this->Ledgers->save($ledger)) {
$this->Flash->success(__('The ledger has been saved.'));
return $this->redirect($this->referer());
}
$this->Flash->error(__('The ledger could not be saved. Please, try again.'));
}
$this->set(compact('ledger'));
}
Here is the relevant frontend code
<?= $this->Form->control('tribe_id[]', ['type' => 'checkbox', 'label' => false, 'class' => 'minimal', 'value' => $tribe->id]) ?>
My question is, what should the field name be for the tribe_id, the idea is, i have a list of checkboxes and the user checks off a couple of boxes and then those tribe_id's get inserted into the LedgersTribes table with the ledger_id
Any ideas on how I can do this?
EDIT: Here is a screenshot of the form
I have reviewed the following links, and none of them answer my question...
CakePHP 3: Save Associated Model Fail
Save associated in cakephp 3 not working
How to save associated joinData in cakephp 3.x
CakePHP 3 cannot save associated model
Cakephp 3 - Save associated belongsToMany (joinTable)
This should do:
echo $this->Form->control('tribes._ids[]', [
'type' => 'checkbox',
'label' => false,
'class' => 'minimal',
'value' => $tribe->id]
]);
This is described here: https://book.cakephp.org/3.0/en/views/helpers/form.html#creating-inputs-for-associated-data
I think you have to get the table (ex: TableRegistry::getTableLocator()->get('')) where you want to save the data. Then create entity from that and save the data, hopefully it will work.

CakePHP using saveAll: How do I save extra data with the HABTM link record?

I have been able to use CakePHP's saveAll method to simultaneously create 'Members' and enroll them in an 'Event' (creating the HABTM link record), which is awesome. For example, this code creates two new 'Members' and adds a record for each of them to the 'EventsMember' table, enrolling them 'Event' 10:
$data = array(
'0' => array(
'Member' => array('email' => 'nobody#nowhere.com'),
'Event' => array('id' => 10)
),
'1' => array(
'Member' => array('email' => 'somebody#nowhere.com'),
'Event' => array('id' => 10)
)
);
$this->Member->saveAll($data);
However, the record in the 'EventsMember' table also has a field called 'role' that holds something like "Presenter" or "Host" or "Attendee" and I would like to save that data when I create the relationship. I tried this and it does not work (the 'EventsMember' 'role' field is always blank):
$data = array(
'0' => array(
'Member' => array('email' => 'nobody#nowhere.com'),
'Event' => array('id' => 10),
'EventsMember' => array('role' => 'Host')
),
'1' => array(
'Member' => array('email' => 'somebody#nowhere.com'),
'Event' => array('id' => 10),
'EventsMember' => array('role' => 'Attendee')
)
);
$this->Member->saveAll($data);
I'm wondering if this is even possible, and if maybe I have to use some kind of callback like beforeSave or afterSave to get this done? I've read that there are some problems with these callbacks when using saveAll, so I'm looking for any tips on what would be the best practice here.
Thanks!
EDIT: I took Adam's advice and made the following changes to my models:
// Event.php
var $hasMany = array('EventsMember');
// Member.php
var $hasMany = array('EventsMember');
// EventsMember.php
var $belongsTo = array('Event', 'Member');
Then in my controller, my code looked almost identical to my second example, except I called the saveAll() method from the EventsMember model, as described in the documentation:
$data = array(
'0' => array(
'Member' => array('email' => 'nobody#nowhere.com'),
'Event' => array('id' => 10),
'EventsMember' => array('role' => 'Host')
),
'1' => array(
'Member' => array('email' => 'somebody#nowhere.com'),
'Event' => array('id' => 10),
'EventsMember' => array('role' => 'Attendee')
)
);
$this->EventsMember->saveAll($data);
The result was no Member or EventsMember records were saved at all. I tried triggering the save from the Member model ($this->Member->saveAll($data);) and this saved the Member records, but not the joining EventsMember records.
I tried removing the pre-existing HABTM associations, and it made no difference. The beforeSave method of the EventsMember model is getting triggered when I call $this->EventsMember->saveAll($data); but it looks like it won't actually save anything.
I'm stymied.
UPDATE: It turns out that no records were created because the joining records were all being created with Event ids and Member ids of 0, which goes against a unique key I have on those two fields combined (that is, no Member can enroll in an Event twice).
Does this suggest that the join model saveAll functionality is not working as documented, since the Member record isn't getting created (meaning there is no Member id to use in the joining record), and the existing Event id is not being passed to the joining EventsMember record either?
VERDICT: I changed the controller to loop on each record and attempt to $this->EventsMember->saveAll($data) for each index of the array, instead of passing the entire array at once. It worked, but was significantly slower than my first example (at the very top). Mind you, I am using transactions, so perhaps using the atomic => false; option would speed things up, while still allowing me to recover from any invalid records.
Bottom line, if you need to save extra data in join table records, you have to process them one at a time. If not, use the method at the top for the best performance.
You can use The Join Model in this case.

CakePHP: How can I customize the labels for multiple checkboxes

I've done a lot of searching but havent been able to solve this yet. I have a user registration form where the user can select a number of store branches as favourites upon registration. I have this relationship set up as a HABTM.
I am able to display the store branches as multiple checkboxes, but I need to display the store name (the store branches belongs to store names) along with the branch name in the label for each checkbox. Something like:
Levi's - Canal Walk
where Canal Walk is the branch name, and Levi's is the store name (coming from a different table)
Can anyone please share some insight on how to do this?
I haven't tested this, but it might work (or help you along the right path):
$branches = $this->Branch->find('list', array(
'fields' => array('Branch.id', 'CONCAT(Store.name, " - ", Branch.name)'),
'joins' => array(
array(
'table' => 'stores',
'alias' => 'Store',
'type' => 'inner',
'foreignKey' => false,
'conditions' => array('Branch.store_id' => 'Store.id')
)
)
));
Set this in your controller, or keep things DRY by placing it in your model in a function such as findFullList()
Hope that helps!
I would do all of this in the view. If your HABTM relationship is setup correctly, a query of the Store model should work something like this:
$stores = $this->Store->findAll();
//Returns
Array([0] => Array(
'Store' => array(),
'Branch' => array(
[0] => array(),
[1] => array()...));
Then pass the $stores variable into the view, and iterate through it with a double nested loop.
foreach($stores as $store){
foreach($store['Branch'] as $branch){
//Code to output checkbox by using $branch['name']
}
}

Refill relationships in form in cakephp in edit mode

I got a controller named Posts, a model called Content which is properly linked with other models such as Category and Location.
In my view for add 'Content' i successfully populate the multi select lists with categories and locations to pick to relate to the post. Saving it all works perfectly.
Now in edit/update mode, I can once again fill the multi selects with categories and locations, but it will not select the ones related to the current post. When looking in the database, there are categories and locations successfully realted to the current post.
This is what I got in my controller:
$this->data = $this->Content->read();
$this->set('locations',$this->Content->Location->find('list',array('fields' => array('id','location'))));
$this->set('categories',$this->Content->Category->find('list',array('fields' => array('id','category'))));
And this is what I got in my view:
echo $this->Form->input('Location', array('type' => 'select','multiple' => 'true','options' => $locations));
echo $this->Form->input('Category', array('type' => 'select','multiple' => 'true','options' => $categories));
What am i missing here? How do i get the already related locations and categories, select in the multi select lists?
(filling of non relationship data, will repopulate textfields etc just perfectly)
Grateful for any help!
Jason
instead of
$this->data = $this->Content->read()
try
$params['conditions'] = array(
'Content.id' => $id
);
$params['contain'] = array(
'Category',
'Location'
);
$this->data = $this->Content->find('first', $params);
You will need the Containable Behaviour for that
Use this:
echo $this->Form->input('Location', array(
'label' => 'Location',
'type' => 'select',
'options' => $LocationArray,
'selected'=> 12(Selected Value)
);

how to create a chain select form in cakephp

My business directory application calls for 3 chained select boxes, and I'm using cakephp to build this application.
The hierarchy and order of choices for the sections is this:
1 - business group
2 - business type
3 - city (included in table customer)
The relationships are:
customer HABTM business types
business groups have many business types
business types have one business group, HABTM customers
I have searched for jquery plugins that help with this, and found one by Remy Sharp, but it doesn't have the more complex relationships I have.
http://remysharp.com/2007/09/18/auto-populate-multiple-select-boxes/
What I imagine happening is the first selection box (business groups) is pre-populated and once a selection is made, an event listener send a message that filters the second selection box, and the same for the third.
What I don't know is how to structure the search action based on the event listener.
Any advice or am I way off base?
As always, I come to the well for help.
Much appreciated.
Paul
Thanks very much Nick, I've read many of your posts I really appreciate your response.
I've followed your instructions but have run into problems. I've tried my best to resolve them but haven't figured it out.
This is what I've done so far:
1) created 'chained' actions in both the business_type and business_directory (renamed customer to business directory, which is more appropriate.)
business type chained action:
function chained($business_group_id) {
$business_types = $this->BusinessType->find('list', array(
'conditions' => array( 'BusinessType.business_group_id' => $business_group_id)
));
$this->set('business_types', $business_types);
}
business directory chained action:
function chained($business_type_id) {
$business_directories = $this->BusinessDirectory->bindModel(array( 'hasOne' => array('business_directories_business_types' )));
$business_directories = $this->BusinessDirectory->find('all', array(
'fields' => array( ' BusinessDirectory.city'),
'conditions' => array( 'business_directories_business_types.business_type_id' => $business_type_id)
));
$this->set('business_directories', $business_directories);
}
I did find that with a HABTM relationship, using find 'list' didn't create the join query, whereas find 'all' did.
2) I then created a search action in the business directory and corresponding view.
For the business groups I created a getList action to populate the option list in the search form:
function getList() {
return $this->BusinessGroup->find('list');
}
In the search view, I've added the javascript for the chain select:
<script type="text/javascript">
<!--
$(function () {
var group = $('#businessGoup');
var type = $('#businessType');
var city = $('#businessDirectoryCity');
type.selectChain({
target: city,
url: '../business_directories/chained/'+$(this).val(),
data: { ajax: true, anotherval: "anotherAction" }
});
group.selectChain({
target: type,
url: '../business_types/chained/'+$(this).val()
}).trigger('change');
});
//-->
</script>
And the form:
create('business_directories', array('action'=>'/search_results')); ?>
input('business_group_id',
array( 'type' => 'select',
'id' => 'businessGoup',
'empty' => '-- Select Business Group --',
'multiple' => true,
'options' => $this->requestAction('/business_groups/getList' ),
'label' => 'Business Group'));
?>
input('business_type.id',
array( 'type' => 'select',
'id' => 'businessType',
'empty' => '-- Select Business Type --',
'multiple' => true,
'options' => 'none selected',
'label' => 'Business Type'));
?>
input('business_directories.id',
array( 'type' => 'select',
'id' => 'businessDirectoryCity',
'empty' => '-- Select City --',
'multiple' => true,
'options' => 'options',
'label' => 'City'));
?>
end('Search'); ?>
When I test the business type chain function, /business_types/chained/1, everything works.
But when I test the search view, I get a javascript alert error. Then when I check firebug, I get the following two errors:
Warning (2): Missing argument 1 for BusinessTypesController::chained() [APP\controllers\business_types_controller.php, line 71]
Notice (8): Undefined variable: business_group_id [APP\controllers\business_types_controller.php, line 73]
Any additional help with this is very much appreciated.
Thanks, Paul
What you need is to have 2 actions in the controllers (business_type and customer).
each action should look like this. In that case for the business type
function chained($parent_id){
$business_types = $this->BusinessType->find('list', array('conditions'=>'BusinessType.business_group_id'=>$parent_id));
$this->set('business_types', $business_types);
}
of course you need also view for that action which will format the values in the proper format for the chained select.
For Business group you need to show all values directly so no ajax is needed.
The Customer controller's action is similar, but you need to select cities of all related customers.
Then with the chained select you need to set the proper elements and set the proper actions which need to be called.
i.e.:
$('#id-of-the-business-group').selectChain({
target: $('#id-of-the-business-type-field'),
url: '/business_types/chained/'+$(this).val()
});

Resources