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();
}
}
?>
Related
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've got a basic script grabbing all posts of children cats of a given category, I am then shuffling the resulting posts and displaying them. So I have added a new child category of cat 4, but I am not getting any of the new posts in that sub cat to show up using my current script. I have tried changing child of and specifically grabbing these by the cat_ID and they show up. As well as just reloading many many times to see if they ever load in but are not in the 30 I am pulling due to the small number of posts in comparison to the posts in other sub cats.
My code is as follows
<?php
$posts = array();
$categories = get_categories( 'child_of=4' );
foreach($categories as $category) {
$args=array(
'showposts' => 30,
'category__in' => array($category->term_id)
);
$posts = $posts + get_posts($args);
} // Close your foreach here
shuffle($posts);
if ($posts) {
foreach($posts as $post) {
setup_postdata($post); ?>
<div <?php post_class('boxy');?>><div class="soc-label" ></div>
<?php
if ( has_post_thumbnail()) {
$full_image_url = wp_get_attachment_image_src( get_post_thumbnail_id(), 'full');
echo '<a href="' . $full_image_url[0] . '" rel="lightbox" title="' . the_title_attribute('echo=0') . '" >';
the_post_thumbnail('thumbnail');
echo '</a>';
}
?>
<?php the_content(''); ?>
</div>
<?php }
}
?>
Thoughts on what I'm missing?
http://codex.wordpress.org/Template_Tags/get_posts
get_posts has an "posts_per_page" argument for posts limiting.
you use "showposts" that is for WP_Query
I am trying to add pagination in CakePHP 2.x with condition search in a form. However, in the View it doesn't give the next page with my condition. Here is my code:
Controller
public function result($palabraclave = null)
{
$this->layout = 'tempart';
$this->paginate = array(
'limit' => 5,
'order' => array(
'Articulo.created' => 'desc'),
'conditions'=>array("titulo like ?"=>"%".$this->data['Paginas']['palabraclave']."%")
)
$data = $this->paginate('Articulo');//Consulta a la base de datos
$this->set(compact('data'));
}
If I click "next" to see the next page result of the search, it doesn't pass on the pagination condition from page 1 to page 2. :(
View:
<div style="display: inline-block; width:70%;" id="test">
<?php foreach ($data as $da): ?>
<br />
<article>
<?php echo $this->Html->image('article_preview/mini_'.$da['Articulo']['imagen'], array('alt' => 'Hospital-Excel'))?>
<div style="text-align:left;">
<?php echo $this->Html->link("<h6 class='artnombre'>".$da['Articulo']['titulo']."<h6>", array('class'=>"artnombre",'action'=> 'articulo',$da['Articulo']['id']),array('escape' => false)); ?>
</div>
<br />
<p>
<?php echo $da['Articulo']['descripcion']; ?> <?php echo $this->Html->link('Read more ....', array('action'=> 'articulo',$da['Articulo']['id'])); ?>
<h5></h5>
</p>
<div class="puntoshorizontal" style="clear"></div>
</article>
<?php endforeach; ?>
<div style="text-align:center">
<?php
// Shows the page numbers
// Shows the next and previous links
echo $this->Paginator->prev('« Back', null, null, array('class' => 'disabled')) . " |";
echo $this->Paginator->numbers(). "| ";
echo $this->Paginator->next('Next »', null, null, array('class' => 'disabled'));
echo "<br />";
echo $this->Paginator->counter();
// prints X of Y, where X is current page and Y is number of pages
?>
</div>
</div>
a way to persist your search params on each page you should do the following:
After you post data, in your method you have $this->data['Paginas']['palabraClave'] then save it the the session. So in each subsequent call to /resultados/pag1,2,..n you get the value of palabra clave from the session.
Finally you would get something like:
public function resultados(){
$conditions = array();
if($this->Session->check('conditions')){
$conditions = $this->Session->read('conditions');
}
if(isset($this->data)){
$conditions = array("titulo LIKE" => "%".this->data['Paginas']'palabraclave']"%",));
//save to the session
$this->Session->write('conditions',$conditions);
}
$data = $this->painate('Articulo',$conditions);
$this->set('data');
}
This may need some adjust but the idea I think is clear.
Set your paginate conditions when calling the paginate method, like this:
$data = $this->paginate('Articulo', array(
"titulo LIKE" => "%" . $this->data['Paginas']['palabraclave'] . "%",
));
as opposed to in the paginate property.
this is example:
1: pass all URL arguments to paginator functions
$this->Paginator->options(array('url' => $this->passedArgs));
2: Common mistake: not keep condition search with paginate
if($this->data){
$this->passedArgs = array(
'name' => $this->data[‘User’]['name']
);
}else{
if(isset($this->passedArgs['name'])){
$this->data[‘User’]['name'] = $this->passedArgs['name'];
}else{
$this->data[‘User’]['name'] = ;
}
}
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