We have two models. Ebooks HABTM Tags, where tags follows the tree behavior.
For each tag we need two numbers. First, the number of ebooks associated to the tag, and secondly the number of ebooks associated to the tag + the number of associated ebooks for each descendant.
How can we get the tags with these numbers in an array in tree format?
Thank you very much for any help.
Update: There is a datetime parameter Ebook.published which defines when the book is to be counted or not. All the ebooks that have codeEbook.published < NOW() should be counted.
Cake has no basic support for this. You will need to do the calculations on the fly or create your own counter cache with custom code to update. This is messy.
I'd suggest overriding the beforeSave() and afterSave() function in your Ebooks controller. If updating, grab the current existing set of tags associated with the Ebook in your beforeSave(). In the afterSave() grab the new set of tags and merge it with the previous set. If there are any changes, iterate through all the tags and call $this->Tag->getPath($id) to get a list of all the ancestors. You'll now have all the tags that were affected by the save. You can now iterate through them and update the counts.
Actually, I found a simpler solution since I already have build the function that returns the tags tree. I used a query for each tag to get the actual count of that moment. Here is what I 've build. Feel free to use it according your needs.
In Tag model
// Returns the number of published ebooks of selected tag
function count_direct_published($tag_id = 0){
$temp = $this->query('SELECT count(*) as count FROM ebooks_tags LEFT JOIN ebooks ON ebooks_tags.ebook_id = ebooks.id WHERE ebooks.published < NOW() AND ebooks_tags.tag_id = '.$tag_id);
return $temp[0][0]['count'];
}
// Returns an array in tree format with $id tag and all his children
// $id = 0 start from the top (parent_id = null), or, from $id = the top's tag id
// $limit = boolean (default false)
// $level = Is the limit of depth applied only if $limit = true
// $ext = true Means this is the first time the function is called
// You can run tree_builder(), returns all the tree,
function tree_builder($id = 0, $limit = false, $level = 1, $ext = 1){
if($ext == 1){
$ext = 0;
$undo = true;
}else{
$undo = false;
}
$this->recursive=-1;
$this->contain('EbooksTag');
$var = array();
$count_all = 0;
// If limit = too big , exit
if($limit !== false && $level > $limit){
return '';
}
// Or else,
// If $id=0, find all the children
if($id == 0){
$tags = $this->find('all',array('conditions'=>array( 'Tag.parent_id IS NULL'), 'order'=>array('Tag.gre')));
// If $id!=0 && runs internally
}elseif($id != 0 && !$undo ){
$tags = $this->find('all',array('conditions'=>array( 'Tag.parent_id'=>$id ), 'order'=>array('Tag.gre')));
}
// If $id!=0 && is called from outside
elseif($id != 0 && $undo){
$tags = $this->find('all',array('conditions'=>array( 'Tag.id'=>$id )));
}
foreach($tags as $key => $tag){
$var[] = $tag;
$next = $this->tree_builder($tag['Tag']['id'], $limit, $level+1, $ext);
end($var); // move the internal pointer to the end of the array
$last_key = key($var); // fetches the key of the element pointed to by the internal pointer
$var[$last_key]['children'] = $next['var'];
$counter_direct = $this->count_direct_published($id);
$var[$last_key]['Tag']['count_all'] = $next['count_all']+$counter_direct;
$count_all += $var[$last_key]['Tag']['count_all'];
}
if( $undo )
{
return $var;
}else{
return array('count_all'=> $count_all, 'var' => $var);
}
}
In tags_controller.php
$this->set('tags', $this->Tag->tree_builder());
In the view
<?php foreach($tags as $tag){?>
<?php // Ο Γονέας σε dropdown box ?>
<div class="main-categ">
<?php echo $tag['Tag']['gre']; ?>
<?php echo $html->image('layout/arrows.png', array('alt'=> "Expand")); ?>
</div>
<div class="collapse">
<?php // Τα στοιχεία του γονέα ?>
<div class="tag-1">
<span class="tag-1">
<?php // Αν ?>
<?php if($tag['Tag']['count_direct']>0){
// Display link
echo $html->link($tag['Tag']['gre'],array('action'=>'view',$tag['Tag']['id']));
echo ' ('.$tag['Tag']['count_direct'].')';
}else{
// Display text
echo $tag['Tag']['gre'];
} ?>
</span>
<?php echo $html->link( 'view all' ,array('action'=>'view_all',$tag['Tag']['id'])); ?>
(<?php echo $tag['Tag']['count_all']; ?>)
</div>
<?php // Για κάθε πρώτο παιδί ?>
<?php foreach($tag['children'] as $tag_1){ ?>
<div>
<span class="tag-2">
<?php if($tag_1['Tag']['count_direct']>0){
// Display link
echo $html->link($tag_1['Tag']['gre'],array('action'=>'view',$tag_1['Tag']['id']));
echo ' ('.$tag_1['Tag']['count_direct'].')';
}else{
// Display text
echo $tag_1['Tag']['gre'];
} ?>
</span>
<?php echo $html->link( 'view all' ,array('action'=>'view_all',$tag_1['Tag']['id'])); ?>
(<?php echo $tag_1['Tag']['count_all']; ?>)
<?php // Τα δεύτερα παιδιά ?>
<?php $i=0; ?>
<?php foreach($tag_1['children'] as $tag_2){ ?>
<?php if($i==0){ echo '<ul class="split">'; $i++; } ?>
<li>
<?php if($tag_2['Tag']['count_direct']>0){
// Display link
echo $html->link($tag_2['Tag']['gre'],array('action'=>'view',$tag_2['Tag']['id']));
echo ' ('.$tag_2['Tag']['count_direct'].')';
}else{
// Display text
echo $tag_2['Tag']['gre'];
} ?>
</li>
<?php } ?>
<?php if($i==1) echo '</ul>'; ?>
<div class="clear"></div>
</div>
<?php } ?>
</div>
Perhaps its not the best solution but it works. Hope that helps
Related
Currently using ACF Repeater for WP to show some posts within a category but if I add the same repeater I want it to keep a log of what post ids have been used so it can exclude them from the new loop.
The only problem is my current code works fine for the first loop and the second but adding anymore than two just resets back to the first set of posts. Dumping the array looks like it is not adding to the array just overwriting it.
First array looks like this
array(3) { [0]=> int(28890) [1]=> int(28790) [2]=> int(28785) }
Second array
array(3) { [0]=> int(28749) [1]=> int(1) [2]=> int(28484) }
Third
array(3) { [0]=> int(28890) [1]=> int(28790) [2]=> int(28785) }
Here is my code
<?php
$cat = get_sub_field('category_name');
$args = array(
'posts_per_page' => 3,
'category_name' => $cat,
'post__not_in' => $ids
);
query_posts( $args );
$ids = array();
?>
<div class="hub-cont">
<?php while (have_posts()) : the_post(); ?>
<?php array_push($ids,get_the_ID()); /*$ids[] = get_the_ID();*/?>
<div class="blockitem2 small-12 medium-4 large-4">
<?php
// Fetch all posts relating to a certain tag then display 4 of them
//Get the Thumbnail URL
$src = wp_get_attachment_image_src( get_post_thumbnail_id($post->ID), array( 720,405 ), false, '' );
?>
<div id="promolink"></div><div class="blockimage" style="background-image: url('<?php echo $src[0]; ?>'); background-repeat: no-repeat; background-size: cover;">
<div class="cats"><?php echo the_category(' '); ?></div>
</div>
<div class="meta">
<a class="gdbnewslink dark" href="<?php echo get_permalink();?>" ><?php the_title();?> </a>
</div>
<div class="clear"></div>
<div id="newsintro"><?php $text = $post->post_content; $trimmed = wp_trim_words( $text, 50, null ); echo $trimmed; ?></div>
</div>
<?php endwhile; ?>
<?php wp_reset_query(); ?>
<?php var_dump($ids); ?>
</div>
Arrays are still pretty new to me so your guidance will be greatly appreciated!
Here is the solution using information from this link. https://www.binarymoon.co.uk/2010/03/5-wordpress-queryposts-tips/
Add this to your functions file.
$bmIgnorePosts = array();
/**
* add a post id to the ignore list for future query_posts
*/
function bm_ignorePost ($id) {
if (!is_page()) {
global $bmIgnorePosts;
$bmIgnorePosts[] = $id;
}
}
/**
* reset the ignore list
*/
function bm_ignorePostReset () {
global $bmIgnorePosts;
$bmIgnorePosts = array();
}
/**
* remove the posts from query_posts
*/
function bm_postStrip ($where) {
global $bmIgnorePosts, $wpdb;
if (count($bmIgnorePosts) > 0) {
$where .= ' AND ' . $wpdb->posts . '.ID NOT IN(' . implode (',', $bmIgnorePosts) . ') ';
}
return $where;
}
add_filter ('posts_where', 'bm_postStrip');
Then to use this you would do your loop as normal, and call ‘bm_ignorePost($post->ID);’ for each post you want to ignore. The following example uses the same query twice, but will display totally different posts on each output.
<?php
// set the query
$query = 'posts_per_page=10';
// loop 1 - display most recent 10 posts
$queryObject = new WP_Query($query);
if ($queryObject->have_posts()) {
while ($queryObject->have_posts()) {
bm_ignorePost($queryPost->post->ID);
$queryObject->the_post();
the_title();
the_content();
}
}
// loop 2 - same query, get the next 10 posts
$queryObject = new WP_Query($query);
if ($queryObject->have_posts()) {
while ($queryObject->have_posts()) {
bm_ignorePost($queryPost->post->ID);
$queryObject->the_post();
the_title();
the_content();
}
}
?>
I'm creating a website using CakePHP. On my main page I want to display the six latest videos and the six most popular videos. I've made an element named 'itemList' that I pass a type parameter and an array with items.
I use the following code for the latest videos:
$this->element('itemList', array('type' => "video", 'items' => $latestVideos));
echo $this->fetch('itemList');
And the following code for the popular videos:
$this->element('itemList', array('type' => "video", 'items' => $popularVideos));
echo $this->fetch('itemList');
The latest videos get displayed just the way they should, but the popular videos first show the latest videos (for the second time) and the popular videos afterwards.
Does anyone know how I can "clear" or "unset" the first itemList element, to start off with an empty one when it gets used for the second time?
<?php
/* The following parameters are set when this element is called upon:
* $type: article, video
* $items: the list with items to be displayed
*/
$this->start('itemList');
echo $this->Html->script('equalColumns');
$itemCount = 0;
$mainType = $type;
$listType = null;
// If the article list is called, use wider columns css (.articleList)
if ($type == "article") {
$listType = " articleList";
}
?>
<ul class="otherArticles<?php print($listType); ?>">
<?php
foreach ($items as $item) {
$duration = null;
$coverImage = null;
$coverImageOffset = array("x" => 0, "y" => 0);
/* If a list of tags is submitted, check for every item if the item is an article or a
* video
*/
if ($mainType == "tag") {
if ($item["TagRelationship"]["article_id"] != null) {
$newItem["Article"]["id"] = $item["TagRelationship"]["article_id"];
$newItem["Article"]["slug"] = $item["Article"]["slug"];
$newItem["Article"]["title"] = $item["Article"]["title"];
$newItem["Article"]["created"] = $item["Article"]["created"];
$newItem["User"] = $item["Article"]["User"];
$newItem["Album"]["Photo"] = $item["Article"]["Album"]["Photo"];
$item = $newItem;
$type = "article";
} elseif ($item["TagRelationship"]["video_id"] != null) {
$type = "video";
}
}
// If a list with videos is supplied, format the duration
if ($type == "video") {
// Set the coverImage
$coverImage = $this->CoverImage->getYouTubeCover($item["Video"]["youtubeId"]);
// If a video lasts shorter than an hour, only show minutes/seconds
if ($item[ucfirst($type)]["duration"] < 3600) {
$duration = gmdate("i:s", $item[ucfirst($type)]["duration"]);
}
// Otherwise show hours as well
else {
$duration = gmdate("H:i:s", $item[ucfirst($type)]["duration"]);
}
} elseif ($type == "article") {
$coverImage = $this->CoverImage->getArticleCover($item["Article"]["id"], $item["Album"]["Photo"]);
$coverImageOffset = $this->CoverImage->getArticleCoverOffset($item["Article"]["id"], $item["Album"]["Photo"]);
}
?>
<li>
<a href="/<?php print($type); ?>s/<?php print($item[ucfirst($type)]["id"]); ?>/<?php print($item[ucfirst($type)]["slug"]); ?>">
<p class="addedDate">Added:
<?php
print($this->Time->timeAgoInWords($item[ucfirst($type)]["created"], array(
'accuracy' => array('minute' => 'minute', 'hour' => 'hour', 'week' => 'week', 'day' => 'day', 'month' => 'month', 'year' => 'year'),
'end' => 'never')));
if ($type == "article") {
?>
by <?php print($item["User"]["username"]); ?>
<?php
}
?>
</p>
<ul class="itemDetails">
<li class="thumb" style="background-image: url(<?php print($coverImage); ?>); background-position: <?php print($coverImageOffset["x"]); ?>% <?php print($coverImageOffset["y"]); ?>%">
<?php
if ($mainType == "tag") {
?>
<p class="label"><?php print(ucfirst($type)); ?></p>
<?php
}
if ($type == "video") {
?>
<p class="duration"><?php print($duration); ?></p>
<?php
}
?>
</li>
<li>
<p><?php print($item[ucfirst($type)]["title"]); ?></p>
</li>
</ul>
</a>
</li>
<?php
}
?>
</ul>
<?php
$this->end();
?>
More than likely you are not unsetting the variables you set in the last element, they are remembered throughout the view. Try unsetting items after the first call -
$this->element('itemList', array('type' => "video", 'items' => $latestVideos));
echo $this->fetch('itemList');
unset($items);
Like the comment said though if this doesn't work we would need to see your element itself to diagnose the problem. If you go into your element itself at the end of everything being processed, unset the array housing all the items you are displaying. This should do the trick.
After some more research I came across the following in the CakePHP Cookbook:
// Clear the previous content from the sidebar block.
$this->assign('sidebar', '');
Sure enough this did the trick.
Thanks for your help though guys!
I have to use a form once in footer and in individual page i.e. index.ctp.
For that I have a database table named contactforms.
I have created a model Contactform.php
<?php
App::uses('AppModel', 'Model');
/**
* Contactform Model
*
*/
class Contactform extends AppModel {
/**
* Validation rules
*
* #var array
*/
var $useTable = false;
public $validate = array(
'firstname' => array(
'notempty' => array(
'rule' => array('notempty')
)
),
'contactno' => array(
'notempty' => array(
'rule' => array('notempty')
)
),
'email' => array(
'notempty' => array(
'rule' => array('notempty')
)
)
);
}
?>
I have a controller from where I am trying to send an email
<?php
App::uses('AppController', 'Controller');
App::uses('CakeEmail', 'Network/Email');
class ContactformsController extends AppController {
public function index() {
$this->Contactform->recursive = 0;
$this->set('contactforms', $this->paginate());
}
public function contact() {
$email = new CakeEmail();
if(isset($this->params['requested']) && $this->params['requested']==true)
{
if ($this->request->is('post'))
{
$this->Contactform->set($this->request->data);
if($this->Contactform->save($this->request->data))
{
$name=$this->request->data['Contactform']['firstname'];
$lastname=$this->request->data['Contactform']['lastname'];
$contact=$this->request->data['Contactform']['contactno'];
$mail= $this->request->data['Contactform']['email'];
$email->from(array($mail => $name));
$email->to('abc#gmail.com');
$message= $this->request->data['Contactform']['message'];
$email->subject('Wombats contact form information');
if($email->send($message))
{
$this->Session->setFlash('Quote Processed..Thank You For Visiting Our Website!!!');
$this->redirect($this->referer());
}
}
}
}
}
}
?>
And then I created an element which I used called in footer and then in index file.
contact.ctp looks like
<?php echo $this->Html->css('contactfooter.css');?>
<?php $contactforms = $this->requestAction('Contactforms/contact') ?>
<div class="frm">
<?php echo $this->Form->create('Contactform'); ?>
<div class="firstrow">
<div class="first">
<?php echo $this->Form->input('firstname',array('label'=>false,'placeholder'=>'firstname','div'=>'firstname','style'=>'width:130px; height:20px;' ));?>
<?php // echo $this->Form->input('firstname',array('label'=>false,'placeholder'=>'firstname','style'=>'width:130px; height:20px; float:left; margin-right:5px;','error'=>array('attributes'=>array('wrap'=>'div','class'=>'errorfirst'))));?>
</div>
<div class="second">
<?php echo $this->Form->input('lastname',array('label'=>false,'placeholder'=>'lastname','div'=>'lastname','style'=>'width:140px; height:20px; '));?>
</div>
</div>
<!--<div class="secondrow">-->
<?php echo $this->Form->input('contactno',array('label'=>false,'placeholder'=>'contactno','div'=>'contactno','style'=>'width:270px; height:20px; margin-bottom:10px;'));?>
<!--</div>-->
<?php echo $this->Form->input('email',array('label'=>false,'placeholder'=>'email','div'=>'email','style'=>'width:270px; height:20px; '));?>
<?php echo $this->Form->input('message',array('label'=>false,'placeholder'=>'message','div'=>'message','style'=>'width:270px; height:25px;margin-top:10px; '));?>
</div>
<!--<br>-->
<div class="sub">
<?php echo $this->Form->end('SUBMIT'); ?>
</div>
When I click submit of one form other form as well validates.
I tried a lot but don't know how to fix Can anyone please help.
EDIT:-
#Nunser I am very confused with these names sorry for that. I have changed my code according to what u told but still its validating one form only.
I have a doubt, according to you I should change in view and elements too but I just have elements.Please can u help I am posting my edited code
I called element from index page as
<?php echo $this->element('Contactform/contact',array('source'=>'index')); ?>\
and from default page as
<?php echo $this->element('Contactform/contact'); ?>
my controller action is
public function contact() {
$email = new CakeEmail();
if(isset($this->params['requested']) && $this->params['requested']==true){
if ($this->request->is('post'))
{
$index = 'Contactform';
if (isset($this->request->data['Contactformindex']))
$index = 'Contactformindex';
$this->Contactform->set($this->request->data[$index]);
if($this->Contactform->save($this->request->data[$index]))
{
$name=$this->request->data[$index]['firstname'];
$lastname=$this->request->data[$index]['lastname'];
$contact=$this->request->data[$index]['contactno'];
$mail= $this->request->data[$index]['email'];
$email->from(array($mail => $name));
$email->to('skyhi13#gmail.com');
$message= $this->request->data[$index]['message'];
$email->subject('Wombats contact form information');
//$email->send($message);
if($email->send($message))
{
$this->Session->setFlash('Quote Processed..Thank You For Visiting Our Website!!!');
//$this->render('/view/elements/quotes/quoteform.ctp');
// $this->autoRender=FALSE;
$this->redirect($this->referer());
}
}
else {
$this->set('formName',$index);
}
}
}
}
In the elements ctp file I changed as
<?php if (!empty($this->validationErrors['Contactform'])) {
$this->validationErrors[$formName] = $this->validationErrors['Contactform'];
}?>
<div class="frm">
<?php
if(isset($source)&& $source == 'index')
echo $this->Form->create('Contactformindex');
else
echo $this->Form->create('Contactform');
?>
<div class="firstrow">
<div class="first">
<?php echo $this->Form->input('firstname',array('label'=>false,'placeholder'=>'firstname','div'=>'firstname','style'=>'width:130px; height:20px;' ));?>
<?php // echo $this->Form->input('firstname',array('label'=>false,'placeholder'=>'firstname','style'=>'width:130px; height:20px; float:left; margin-right:5px;','error'=>array('attributes'=>array('wrap'=>'div','class'=>'errorfirst'))));?>
</div>
<div class="second">
<?php echo $this->Form->input('lastname',array('label'=>false,'placeholder'=>'lastname','div'=>'lastname','style'=>'width:140px; height:20px; '));?>
</div>
</div>
<!--<div class="secondrow">-->
<?php echo $this->Form->input('contactno',array('label'=>false,'placeholder'=>'contactno','div'=>'contactno','style'=>'width:270px; height:20px; margin-bottom:10px;'));?>
<!--</div>-->
<?php echo $this->Form->input('email',array('label'=>false,'placeholder'=>'email','div'=>'email','style'=>'width:270px; height:20px; '));?>
<?php echo $this->Form->input('message',array('label'=>false,'placeholder'=>'message','div'=>'message','style'=>'width:270px; height:25px;margin-top:10px; '));?>
</div>
<!--<br>-->
<div class="sub">
<?php echo $this->Form->end('SUBMIT'); ?>
</div>
Using this code still it validated one form only and form is that which I have called without source as index and when clicked on index submit button it validates the other form. I am not sure as do I have to use the same Fromindex name as specified by you, does that matter. I am not able to find as where I am going wrong.Please help and Thanks in advance.
First, why are you doing this
<?php $contactforms = $this->requestAction('Contactforms/contact') ?>
in your element? I don't see the use of that requestAction except slowing down the processing...
Ok, but your problem...
Since you're calling an element, there's no easy way to avoid the validation of both forms. Usually, when there are two forms corresponding to the same model, the way to not validate both is to change the "name" of the form like this
<!--in the first form-->
<?php echo $this->Form->create('Contactform1'); ?>
<!--in the second form-->
<?php echo $this->Form->create('Contactform2'); ?>
But, since you are creating that form with an element, there's no easy way of changing the name of the form in one place and not in the other...
So this is what you should do (there may be other ways of doing what you want, but this is the one I can think of). When you call the contact.ctp element, pass a variable to it
echo $this->element('contact', array(
"source" => "index"
));
With that, you know you're calling the element from the index.ctp, for example. Then, in the element, change the form declaration depending on the variable
//the beginning of your element
<?php
if (isset($source) && $source == 'index')
echo $this->Form->create('FromIndex');
else
echo $this->Form->create('FromContact');
?>
You'll also need to modify your action, to read the data in FromIndex or in FromContact, depending on where it came from
public function contact() {
$email = new CakeEmail();
if(isset($this->params['requested']) && $this->params['requested']==true) {
if ($this->request->is('post')) {
//change the index of the array depending on where it came form
$index = 'FromContact';
if (isset($this->request->data['FromIndex']))
$index = 'FromIndex';
$this->Contactform->set($this->request->data[$index]);
if($this->Contactform->save($this->request->data[$index]))
{
$name=$this->request->data[$index]['firstname'];
$lastname=$this->request->data[$index]['lastname'];
$contact=$this->request->data[$index]['contactno'];
$mail= $this->request->data[$index]['email'];
$email->from(array($mail => $name));
$email->to('abc#gmail.com');
$message= $this->request->data[$index]['message'];
$email->subject('Wombats contact form information');
if($email->send($message))
{
$this->Session->setFlash('Quote Processed..Thank You For Visiting Our Website!!!');
$this->redirect($this->referer());
}
}
}
}
}
}
When you save or set something (unless is with saveAssociated, saveAll or those kind of functions, when you're saving more than one model), there's no need to specify the model in the array to be saved.
$saveMe = array('User'=>array('name'=>'John'));
$saveMeToo = array('name'=>'John');
$this->User->save($saveMe); //will work
$this->User->save($saveMeToo); //will work too
That's why the change of $this->request->data indexes will work either way. But the validation will be for the specific index (FromContact or FromIndex).
Note: I haven't tested the code, so be sure to check for missing parenthesis, unclosed ' and those kind of things.
EDiT
#winnie pointed out that the validation only happened for one form, and that's because I overlooked something. The validation errors get displayed in the view if there's something set in this variable $this->validationErrors['ModelName'] in the view. And that's what I missed. So, re-change the action to pass the $index of the model to the view, so the view knows which form called the action, like this
public function contact() {
$email = new CakeEmail();
if(isset($this->params['requested']) && $this->params['requested']==true) {
if ($this->request->is('post')) {
//change the index of the array depending on where it came form
$index = 'FromContact';
if (isset($this->request->data['FromIndex']))
$index = 'FromIndex';
$this->Contactform->set($this->request->data[$index]);
if($this->Contactform->save($this->request->data[$index]))
{
//this if is exactly the same as the other one
} else {
//now, if there's no save, there's some validation errors
//tell the view which form called this save
$this->set('formName', $index);
}
}
}
Now in the view you need to copy the errors you get in the model, to the "fake model name" we gave the form. In the first line of your view do this (and in the element too)
if (!empty($this->validationErrors['Contactform'])) {
$this->validationErrors[$formName] = $this->validationErrors['Contactform'];
}
I am trying to adapt the Wordpress loop to style posts differently in rows that get
infinitely smaller until all posts are displayed
The concept here is to display posts in rows
first row 1 post
second row 2 posts
third row 3 posts
fourth row 4 posts
fifth row 5 posts
sixth row 6 posts
seventh row 7 posts
and onward until all posts have been retrieved
the below code is limited and does not do the above how would you adapt to make the below do the above?
the below code is functional and can be seen here: http://ccs.btcny.net/redhook/
<?php if (have_posts()) : ?>
<?php $count = 0; ?>
<?php while (have_posts()) : the_post(); ?>
<?php $count++; ?>
<?php if ($count == 1) : ?>
<div class="style-1"><?php the_content(); ?></div>
<?php elseif ($count == 2 || $count == 3) : ?>
<div class="style-2"><?php the_content(); ?></div>
<?php elseif ($count == 4 || $count == 5 || $count == 6) : ?>
<div class="style-3"><?php the_content(); ?></div>
<?php elseif ($count == 7 || $count == 8 || $count == 9 || $count == 10) : ?>
<div class="style-4"><?php the_content(); ?></div>
<?php elseif ($count == 11 || $count == 12 || $count == 13 || $count == 14 || $count == 15 ) : ?>
<div class="style-5"><?php the_content(); ?></div>
<?php elseif ($count >= 16 ) : ?>
<div class="style-6"><?php the_content(); ?></div>
<?php endif; ?>
<?php endwhile; ?>
Here is my solution. Basically I am setting the row number by checking if the next element is inside the current row. To do that I need to see if next index is equal to the first index of the next row.
The tricky part is to find the index of the first item in the row. As this is not linear but geometrical order the items number in each row will be increasing. To calculate the first item of the next row I need to add the number of elements in the current row (in this case simply the current row) to the first index of the current row. When I get the first index of the next row and when the next item will be equal to that number I need to go to the next row and set the first index for the current row for that calculated index.
<?php //The first index of the first row is 1?>
<?php $row = 1;?>
<?php $curFirstIndex = 1;?>
<?php //start counting items ?>
<?php $count = 0;?>
<?php while (have_posts()) : the_post();?>
<?php //create actual html code ?>
<div class="style-<?php echo $row;?>"><?php the_content(); ?></div>
<?php //check if the next item is equal to the first index of the next row ?>
<?php if ($count+1 >= ($row + $curFirstIndex)) :
<?php
//the next item will be in the new row
//in $curFirstIndex store the first number of the current row
$curFirstIndex = $row + $curFirstIndex;
//go to the next row
$row++;
?>
<?php endif; ?>
<?php $count++;?>
<?php endwhile;?>
[EDIT]
The above solution works but it has one little flaw, namely it incorporates the logic into the template. Wordpress does not follow some strict layer separation but in my opinion it is always good to try to do that if it is possible. So I come up with two ways that can reduce the logic of this in the template file and move the logic outside. Since wordpress has functions.php in its theme we can use this file to create a function which will handle that logic. Here is the code
Template view file:
<?php //Initial values?>
<?php $row = 1;?>
<?php $count = 1;?>
<?php while (have_posts()) : the_post();?>
<?php //create actual html code ?>
<div class="style-<?php echo $row;?>"><?php the_content(); ?></div>
<?php //check if the current item is the last in the row ?>
<?php if ($count == getRowLastItemIndex($row)) {$row++;} ?>
<?php $count++;?>
<?php endwhile;?>
in functions.php:
function getRowLastItemIndex($row) {
$index = 0;
for($i = 1; $i <= $row; $i++) {
$index += $i;
}
return $index;
}
Or make it even more separate in the following way
The template file
<?php //Initial values?>
<?php $row = 1;?>
<?php $count = 1;?>
<?php while (have_posts()) : the_post();?>
<?php //create actual html code ?>
<div class="style-<?php echo $row;?>"><?php the_content(); ?></div>
<?php $row = getRowNumber($row,$count);?>
<?php $count++;?>
<?php endwhile;?>
And add another function to the functions.php
function getRowNumber($row,$count) {
if ($count == getRowLastItemIndex($row))
$row++;
}
return $row;
}
What an elegant idea! Try something like this:
<?php
if (have_posts()):
$count = 0;
while (have_posts()):
$count++;
?>
<div class="style-<?php echo $count; ?>">
<?php
for( $i = 1; $i <= $count; $i++ ):
if (have_posts()):
the_post();
the_content();
endif;
endfor;
?>
</div>
<?php
endwhile;
?>
A while loop count posts and output <div>'s. Inside div executes for body as many times, as current $count value have. So for $count = 1 loop executes 1 time, then for $count = 2 loop executes 2 times and so on. Inside loop wp function the_post() iterate post index and the_content() output current post.
This code is only updating one row (the one that matches the first in the array):
Edit: showing more code!
Here's my view. It's grabbing db entries based on an association (there's one model "clients" that has a one-to-many relationship with two other models, Programs and Users).
<div class="view">
<h2><?php echo h($program['Program']['title'])?></h1>
<?php echo $this->Form->create('User', array('action' => 'pushing')); ?>
<table id="pushTable">
<tr><th colspan="5">Select the athletes you would like to push this program to:</th></tr>
<tr>
<?php $i=0; ?>
<?php foreach ($clients['Players'] as $player) {?>
<?php if ($i == 0) {?><tr><?php } ?>
<td><?php echo $this->Form->checkbox($player['id']) . ' ' . $player['username'];?></td>
<?php if ($i == 4) {?></tr><?php $i = 0; } else { $i++; } ?>
<?php } ?>
</tr>
</table>
<?php echo $this->Form->end(__('Push')); ?>
</div>
In my Users controller:
public function pushing() {
if ($this->request->is('post') || $this->request->is('put')) {
$athletes = $this->request->data['User'];
foreach ( $athletes as $player => $flag){
if ($flag == 0){
unset($athletes[$player]);
}
}
$this->User->updateAll(
array('User.data' => "'foo'"),
array('User.id' => $athletes)
);
$this->Session->setFlash(__('Programs were pushed!'));
}
}
}
$athletes is the array is collected from checked boxes, and there seem to me no problems with that... so I'm not sure why the updateAll isn't iterating over each ID in the array...
Maybe it's not working for a db related reason? I'm developing on MAMP... maybe the db isn't set up for "atomic" stuff (only heard about that here today!).
I had previously tried using a foreach to loop over the id's, then in the foreach just did this (edit: more code!)
public function pushing() {
if ($this->request->is('post') || $this->request->is('put')) {
foreach ($this->request->data['User'] as $player => $flag) {
if ($flag) {
$this->User->id = $player; // set ID to player we want to save ($player is id that was suplpied in view to checkbox
$this->User->saveField('data', 'foo'); // then, push the data!
}
}
$this->Session->setFlash(__('Programs were pushed!'));
}
$this->autoRender = false;
$this->redirect(array('action' => 'index'));
}
But that caused weird results with redirection. No matter where I put redirection in that action, the save just wanted to do its own thing. That $this->autoRender did nothing. The controller still tried to resolve to the /pushing/ route
Give this a shot :)
In your view:
<div class="view">
<h2><?php echo h($program['Program']['title'])?></h1>
<?php echo $this->Form->create('User', array('action' => 'pushing')); ?>
<table id="pushTable">
<tr><th colspan="5">Select the athletes you would like to push this program to:</th></tr>
<tr>
<?php $i=0; ?>
<?php foreach ($clients['Players'] as $player) {?>
<?php if ($i == 0) {?><tr><?php } ?>
<td><?php echo $this->Form->input('Player.' . $player['id']. '.id', array('type' => 'checkbox', 'value' => $player['id'])) . ' ' . $player['username'];?></td>
<?php if ($i == 4) {?></tr><?php $i = 0; } else { $i++; } ?>
<?php } ?>
</tr>
</table>
<?php echo $this->Form->end(__('Push')); ?>
</div>
In your controller:
public function pushing() {
if ($this->request->is('post') || $this->request->is('put')) {
$athletes = $this->request->data['Player'];
$athlete_ids = array();
foreach($athletes as $a){
$athlete_ids[$a['id']] = $a['id'];
}
$this->User->updateAll(
array('User.data' => "'foo'"),
array('User.id' => $athlete_ids)
);
$this->Session->setFlash(__('Programs were pushed!'));
}
}
}
Hi had to change the code a bit to make it for work me..
$contactsids = array();
foreach($selected as $key => $val){
if(($val <> '') && ($val <> 0) ) {
$contactsids[$val] = $val;
}
}
$this->Contact->updateAll(
array('Contact.contact_category_id' => $category_id),
array('Contact.id' => $contactsids)
);