I am a newbie in Drupal. Please accept my ignorance. I have a page with a custom hook to override the view and display list of posts with default sorting or no sorting. I want to sort the array based on the node create date. But not sure where to plug the sorting argument. Can anyone please help? I have this code block in my_view.module file.
function _sites_by_park_page_get_node_references($fieldName, $property, $park_id) {
$results = array();
$parks = _sites_by_park_page_get_sites($park_id);
foreach ($parks['features'] as $feature) {
foreach($feature['properties'][$property] as $key => $value) {
if (!array_key_exists($key, $results)) {
$query = new EntityFieldQuery();
$query->entityCondition('entity_type', 'node');
$query->fieldCondition($fieldName, 'value', $value);
$result = $query->execute();
$node = node_load(array_shift(array_keys($result['node'])));
$results[$key] = $node->title;
}
}
}
return $results;
}
/**
* Return GeoJSON formatted park data
*/
function _sites_by_park_page_get_sites($park_id) {
// Uses a lot of memory thanks to views and GD image resizing, could use some garbage collection opimisation
ini_set('memory_limit', '512M');
$data_view = views_get_view('sites_by_park_data');
$data_view->set_arguments(array($park_id));
$data_view->execute('page');
// Build our own, more lightweight geojson structure for faster page loads (Leaflet module generated 2MB of data vs this custom module's 40kb-ish for 140 parks)
$results = array(
'type' => 'FeatureCollection',
'features' => array(),
);
foreach($data_view->result as $result) {
// Initialise some arrays that may be merged between result sets (for instance when multiple activities contribute to a site's overall data)
if (!isset($results['features'][$result->nid])) {
$results['features'][$result->nid] = array(
'type' => 'Feature',
'properties' => array()
);
}
}
$results['features'] = array_values($results['features']);
return $results;
}
As per MilanG's suggestion I have done this through admin section. Change the sorting order and it works fine now.
Related
I'm fairly new to Drupal, and I need your help on this issue:
I have built a module on Drupal 7 which makes a database query to extract the name and the email of all users. The code is this:
<?php
/**
*
*/
function myexample_block_info() {
$blocks['myblock'] = array(
'info' => t('My Custom Modue'),
);
return $blocks;
}
function myexample_block_view($delta = '') {
$block = array();
$results = db_select('users','a')
->fields('a', array('name', 'mail'))
->execute();
$header = array(t('NAME'), t('MAIL'));
$rows = array();
foreach ($results as $node) {
$rows[] = array(
$node->name,
$node->mail,
);
}
$block['content'] = theme('table', array('header' => $header, 'rows' => $rows));
return $block;
}
I put the block into "Side-bar first"-Bartik theme. The code works and it retrieves what I want. But the problem with the display. I'm getting the results repeated 3 times:
Results
Why am I getting the results repeated three times. I cant seem to find anything wrong with the code. Could anyone help me please? Thanks in advance.
What are you trying to achieve ? If you are just trying to extract data from the DB this is not the way to go. You should extract data via mysql client directly.
How I can pass multiple field values through $data and render them using $items.
Please see the below code snippet:
function custom_block_view($delta = '') {
global $user;
$account = $user;
$block['content'] = t('Hello #user from IP #host',array(
'#user' => format_username($user),
'#host' => $account->hostname
));
$result = db_select('node','a')
->fields('a', array('title', 'nid', 'status'))
->execute();
foreach($result as $node) {
$items[] = array(
'data' => t($node->title)
);
}
$block['content'] .= theme('item_list', array(
'items' => $items
));
return $block;
}
I found the how to pass another value through $data. Just adding a simple concatenation symbol (.)
Please check the below snippet.
foreach($result as $node) {
$items[] = array(
'data' => t($node->title).t($node->nid)
);
}
You can only use renderable array or string containing rendered HTML content.
content: The content of the block's body. This may be a renderable array (preferable) or a string containing rendered HTML content.
You can refer this link for more info:
hook_block_view
Drupal 7 Render Arrays
Altering
Both blocks and pages can be altered just as forms have been alterable for some time. Many other types are also alterable. With hook_page_alter() we can do things like this:
function mymodule_page_alter(&$page) {
// Move search form into the footer.
$page['footer']['search_form'] = $page['sidebar_first']['search_form'];
unset($page['sidebar_first']['search_form']);
// Remove the "powered by Drupal" block
unset($page['footer']['system_powered-by']);
}
I'm working on an edit method. After saving data, an email is sent out with the changes made during the edit. Everything works except for one infuriating but crucial bug. Here it is distilled down very simply:
$data = $this->SupportTicket->readForView($st_id);
$this->SupportTicket->id = $st_id;
if ($this->SupportTicket->save($this->request->data)) {
//call custom model method to pull view data
$data = $this->SupportTicket->readForView($st_id);
//do something with this data
}
The issue is that $data comes out with the pre-save data. So what I then try to do with the new data doesn't work.
I can't just use $this->request->data because it doesn't have the full data that I want in it.
The save does however work. If I refresh the view method for the same record, it shows as updated. So it's saving, but when I do the find after saving it is giving me old data.
Any ideas?
Update: it doesn't happen with findById($st_id) so it must be something to do with my custom method. Code:
public function readForView($id)
{
$data = $this->find('first', array(
'conditions' => array(
'SupportTicket.id' => $id
),
'contain' => array(
'User',
'Owner'
)
));
if (empty($data)) {
throw new notFoundException('Ticket not found');
}
$data['SupportTicket']['type_long'] = $this->getLongType($data['SupportTicket']['type']);
$data['SupportTicket']['status_long'] = $this->getLongStatus($data['SupportTicket']['status']);
$data['SupportTicket']['name'] = 'Support Ticket #' . $data['SupportTicket']['id'] . ' - ' . $data['SupportTicket']['title'];
return $data;
}
Copying the code from this method into the Controller gives the same result.
I've found this helpful: https://edivad.wordpress.com/2008/04/15/cakephp-disable-model-queries-caching/
By model:
class Project extends AppModel {
var $cacheQueries = false;
...
By function:
function someFunction {
$this->Model->cacheQueries = false;
...
try using last Insert ID
$id=$this->getLastInsertID();
public function readForView($id)
I currently have index, view, add and edit from my tickets/views and am planning to add another view called current. I want to display only the "Resolved" tickets on that view but have no idea how to do it. I've been trying to figure this out for the past couple of days with no luck. Where should I put the "find" code and what should I include on my tickets/current view code? Here's what I have tried so far:
/controllers/tickets_controller
function current() {
$current = $this->set('tickets', $this->Ticket->find('all', array(
'conditions' => array('Ticket.status' => 'Resolved'),
'order' => array('Ticket.created' => 'desc')
)));
$this->set('tickets', $this->paginate());
}
/views/tickets/current.ctp
<?php
$i = 0;
foreach ($tickets as $ticket):
$class = null;
if ($i++ % 2 == 0) {
$class = ' class="altrow"';
}
?>
This code displays the same as /views/tickets/index.ctp (with all the records from the table).
Thanks,
Lyman
You were almost there. $this->set('foo') in Cake is one way you can pass variables to the view.
What the code below does is set a variable called current the return value of the find method which has a custom condition, which can be accessed from the view. (It's an array in this case; but you can set anything)
You don't need the paginate() here unless you plan to use pagination in this view.
So something like this should do the trick ($curr is a bad variable name but I wasn't feeling inventive. (resolved_tickets would make more sense (why current?))
//controllers/tickets_controller
function current() {
$this->set('current', $this->Ticket->find('all',
array('conditions' => array('Ticket.status' => 'Resolved'),
'order' => array('Ticket.created' => 'desc')
)));
}
/views/tickets/current.ctp
<?php
$i = 0;
// adjust the variable in the foreach
foreach ($current as $curr):
$class = null;
if ($i++ % 2 == 0) {
$class = ' class="altrow"';
}
echo $curr['Ticket']['id']; // example
?>
I have and existing application in CakePHP with a database.
The task is to apply translate behavior to its models. The problem is that i18n.php script just creates _i18n table but doesn't copy existing data to this table.
Don't you know any script that could do that?
Thanks for any help.
I extended the answers from Aziz and MarcoB and created a even more generic CakeShell out of it.
In the method _execute() simply set something like:
$this->_regenerateI18n('BlogPosts', array('title'), 'deu');
And all entries for the Model BlogPosts for the column title in the language deu will be create in the i18n table.
This is CakePHP 2.4 compatible!
<?php
class SetuptranslationsShell extends AppShell {
public function main() {
$selection = $this->in('Start to create translated entries?', array('y', 'n', 'q'), 'y');
if (strtolower($selection) === 'y') {
$this->out('Creating entries in i18n table...');
$this->_execute();
}
}
function _execute() {
$this->_regenerateI18n('BlogPosts', array('title'), 'deu');
$this->_regenerateI18n('BlogTags', array('name'), 'deu');
}
/**
* See http://stackoverflow.com/q/2024407/22470
*
*/
function _regenerateI18n($Model, $fields = array(), $targetLocale) {
$this->out('Create entries for "'.$Model.'":');
if (!isset($this->$Model)) {
$this->{$Model} = ClassRegistry::init($Model);
}
$this->{$Model}->Behaviors->disable('Translate');
$out = $this->{$Model}->find('all', array(
'recursive' => -1,
'order' => $this->{$Model}->primaryKey,
'fields' => array_merge(array($this->{$Model}->primaryKey), $fields))
);
$this->I18nModel = ClassRegistry::init('I18nModel');
$t = 0;
foreach ($out as $v) {
foreach ($fields as $field) {
$data = array(
'locale' => $targetLocale,
'model' => $this->{$Model}->name,
'foreign_key' => $v[$Model][$this->{$Model}->primaryKey],
'field' => $field,
'content' => $v[$Model][$field],
);
$check_data = $data;
unset($check_data['content']);
if (!$this->I18nModel->find('first', array('conditions' => $check_data))) {
if ($this->I18nModel->create($data) AND $this->I18nModel->save($data)) {
echo '.';
$t++;
}
}
}
}
$this->out($t." entries written");
}
}
As far as I know, there's no way to do this. Moreover, because of the way the i18n table is configured to work, I think there's a better solution. A while back, I wrote a patch for the TranslateBehavior that will keep you from having to copy existing data into the i18n table (that felt insanely redundant to me and was a huge barrier to implementing i18n). If no record for that model exists in the i18n table, it will simply read the model record itself as a fallback.
Unfortunately, the Cake team appears to have moved everything to new systems, so I can no longer find either the ticket or the patch that I submitted. My patched copy of the TranslateBehavior is in my Codaset repository at http://codaset.com/robwilkerson/scratchpad/source/master/blob/cakephp/behaviors/translatable.php.
As you might expect, all of the usual warnings apply. The patched file was developed for 1.2.x and works for my needs, by YMMV.
try to use it
function regenerate()
{
$this->Article->Behaviors->disable('Translate');
$out = $this->Article->find('all', array('recursive'=>-1, 'order'=>'id'));
$t = $b = 0;
foreach($out as $v){
$title['locale'] = 'aze';
$title['model'] = 'Article';
$title['foreign_key'] = $v['Article']['id'];
$title['field'] = 'title';
$title['content'] = $v['Article']['title'];
if($this->Article->I18n->create($title) && $this->Article->I18n->save($title)){
$t++;
}
$body['locale'] = 'aze';
$body['model'] = 'Article';
$body['foreign_key'] = $v['Article']['id'];
$body['field'] = 'body';
$body['content'] = $v['Article']['body'];
if($this->Article->I18n->create($body) && $this->Article->I18n->save($body)){
$b++;
}
}
}
Thanks Aziz. I modified your code to use it within the cakeshell
(CakePHP 2.3.8)
function execute() {
$this->out('CORE_PATH: '. CORE_PATH. "\n");
$this->out('CAKEPHP_SHELL: '. CAKEPHP_SHELL. "\n");
$this->out('Migrate BlogPosts');
$this->regenerateI18n('BlogPost', 'title', 'BlogPostI18n');
}
/**
* #param string $Model
* #param string $Field
* #param string $ModelI18n
*/
function regenerateI18n($Model = null, $Field = null, $ModelI18n = null)
{
if(!isset($this->$Model))
$this->$Model = ClassRegistry::init($Model);
if(!isset($this->$ModelI18n))
$this->$ModelI18n = ClassRegistry::init($ModelI18n);
$this->$Model->Behaviors->disable('Translate');
$out = $this->$Model->find('all', array('recursive'=>-1, 'order'=>'id'));
$t = 0;
foreach($out as $v){
$data = array(
'locale' => 'deu',
'model' => $this->$Model->name,
'foreign_key' => $v[$Model]['id'],
'field' => $Field,
'content' => $v[$Model][$Field],
);
if($this->$ModelI18n->create($data) && $this->$ModelI18n->save($data)){
echo '.';
$t++;
}
}
$this->out($t." Entries written");
}