Jump to content

Testing for the first element in a wirearray


thetuningspoon
 Share

Recommended Posts

I'm creating a news section on my homepage which outputs a list of article previews with an image, link, and summary for each. I have this working perfectly. However, I want to have the first article in the list have a larger image and some different classes for styling. Here is my code, which uses first() on the $newsChildrenArray array to test against the current item ($newsChildPage) in the foreach loop and create a larger image if they match up:

foreach($newsChildrenArray as $newsChildPage) {

 //Test for an image on the current page
 if($newsChildPage->images->eq(0) != null) {
  //If this is the first article, make a larger image
  if( $newsChildPage == $newsChildrenArray->first() ) {
$newsImg = $newsChildPage->images->first()->size(157,151);
  }
  else {
$newsImg = $newsChildPage->images->first()->size(77,71);
  }
 }

 echo '
  <a href="' . $newsChildPage->url . '">
<li '; if($newsChildPage == $newsChildrenArray->last())echo 'class="last"'; echo '>';

if($newsImg) {
 echo '<img src="' . $newsImg->url . '" alt="' . $newsImg->description . '" />';
}

echo '
 <p>
  <span class="preview-title">' . $newsChildPage->title . '</span> • <span class="preview-date">' . $newsChildPage->date . '</span>
  <br />' . $newsChildPage->summary . '
    <a href="' . $newsChildPage->url . '" class="read-more">Read More...</a>
 </p>
</li>
  </a>
 ';
}

Unfortunately, ProcessWire chokes on the test. It echos the first element in the array correctly and then spits out "Fatal error: Nesting level too deep - recursive dependency?"

Strangely, it has no problem with the similar test I perform later on using last() to output a class on the last array element.

I created a simple workaround by adding a counter to the foreach loop and testing for the first element in the loop, but I'm curious--for future reference and a better understanding of the API--why this would be failing. Is there a better way of doing this that would still utilize the PW API?

Link to comment
Share on other sites

The problem lays in the

 if( $newsChildPage == $newsChildrenArray->first() ) {

Comparing object shoudl be done using "==="

Not sure why the error, but with "===" it works.

However I cleaned up a little your code, using heredoc (<<<_END) which can be handy to generate output code so the indentation stays.

foreach( $newsChildrenArray as $newsChildPage ) {
$newsImg = '';
//Test for an image on the current page
if( count($newsChildPage->images) ) {
	//If this is the first article, make a larger image
	if( $newsChildPage === $newsChildrenArray->first() ) {
		$newsImg = $newsChildPage->images->first()->size(157,151);
	} else {
		$newsImg = $newsChildPage->images->first()->size(77,71);
	}
}
$class = '';
$imgstr = '';
if( $newsChildPage === $newsChildrenArray->last() ) $class = ' class="last"';
if( $newsImg ) {
	$imgstr = "<img src='{$newsImg->url}' alt='$newsImg->description'/>";
}
$out = <<<_END

<a href="{$newsChildPage->url}">
<li$class>$imgstr
	<p>
		<span class="preview-title">{$newsChildPage->title}</span>
		• <span class="preview-date">{$newsChildPage->date}</span><br />
		{$newsChildPage->summary}
		  <a href="{$newsChildPage->url}" class="read-more">Read More...</a>
	</p>
</li>
</a>

_END;
echo $out;
}

Also changed some things to avoid further problems. Using ->eq(0) for example to check if there's an image is wrong. It won't work if there no image. Use count();

Also the check if($newsImg) will fail if there's no image as the variable won't exists. So you need to set it before.

  • Like 3
Link to comment
Share on other sites

Always forget about it but thought I'd mention it. You can also use

if( $newsChildPage->id == $newsChildrenArray->first()->id ) { ...

which is even better and should be faster although not sure how much really. :)

Link to comment
Share on other sites

Another option I learned on the forums here is to use a do while loop if you want to display the first item differently than the following.

$newsItemList = $page->children;
$newsItemListCount = count($newsItemList);
$cnt = 0;
do {
$cnt++;
$newsItem = $newsItemList->shift();

if ($cnt == 3) {
 // do stuff
} else {
 // do stuff
}
} while(count($newsItemList));

Link to comment
Share on other sites

Thanks arjen for posting this. Well it has nothing really to do with the while loop it's just another variant. It can be done with a foreach or for loop

$newsItemList = $page->children;

$cnt = 0;
foreach($newsItemList as $news) {
 $cnt++;
 if ($cnt == 1) {
 // do stuff
 } else {
 // do stuff
 }
}

Link to comment
Share on other sites

Thanks a lot Soma. Using a counter like Arjen suggested is the workaround I am currently using, but I think I will use your code because I like it better :)

Also the check if($newsImg) will fail if there's no image as the variable won't exists. So you need to set it before.

Well, I want to check whether it exists and do nothing if it doesn't. I suppose I should use isset()?

Link to comment
Share on other sites

I think it might be a little better to use a counter just because that is native PHP with no function call (i.e. always faster/more efficient). And you should be able to get that counter right out of the foreach().

foreach($newsChildrenArray as $cnt => $newsChildPage) {
 $newsImg = $newsChildPage->images->first();
 if($cnt == 0 && $newsImg) {
   $newsImg = $newsImg->size(157,151); // big
 } else if($newsImg) {
   $newsImg = $newsImg->size(77,71); // small
 }
 // the rest…
}
  • Like 1
Link to comment
Share on other sites

I ended up using:

if( $newsChildPage->id === $newsChildrenArray->first()->id )

to keep thing consistant since I am also using last(), which can't be found (I don't think?) with the counter method.

Thanks for demonstrating how to use the counter with foreach, Ryan. Didn't know about that one!

If anyone is interested in using something like this on their own project, here is my updated code, which includes the logic for removing articles that we don't want in our list (making use of a custom checkbox on news article pages). Instead of using the heredoc syntax, I tried to go with more separation of the markup and php. Not sure if I succeeded or not :)

<ul class="news-previews">
<?php
//Create an array from the children of the news page
$newsPage = $pages->get("/news/");
$newsChildrenArray = $newsPage->children;

//Remove articles we don't want published on the homepage from the array
foreach($newsChildrenArray as $newsChildPage) {
 if($newsChildPage->publishOnHomepage == false) {
  $newsChildrenArray = $newsChildrenArray->remove($newsChildPage);
 }
}

//Loop through the modified array and output the articles
foreach($newsChildrenArray as $newsChildPage) {

 //Grab the first image in the article, if one exists
 $newsImg = '';
 if( count($newsChildPage->images) ) {
  //If this is the first article, make a larger image
  if($newsChildPage->id === $newsChildrenArray->first()->id) {
   $newsImg = $newsChildPage->images->first()->size(150,150);
  }
  else {
   $newsImg = $newsChildPage->images->first()->size(57,51);
  }
 }

 //Set a class if this is the first or last item
 $class = '';
 if( $newsChildPage->id === $newsChildrenArray->first()->id ) $class = 'class="first"';
 if( $newsChildPage->id === $newsChildrenArray->last()->id ) $class = 'class="last"';

 //Markup time
 ?>

 <a href="<?php echo $newsChildPage->url ?>">
  <li <?php echo $class ?>>
   <?php if($newsImg)echo '<img src="'.$newsImg->url.'" alt="'.$newsImg->description.'"/>'; ?>
   <p>
 <h4 class="preview-title">
  <?php echo $newsChildPage->title ?> • <span class="preview-date"><?php echo $newsChildPage->date ?></span>
 </h4>
 <?php echo $newsChildPage->summary ?>
 <a href="<?php echo $newsChildPage->url ?>" class="read-more">Read More...</a>
   </p>
  </li>
 </a>

<?php
}
?>
</ul>
Link to comment
Share on other sites

I think in this case correct would be == not === but both will work. There's some coverage of it here http://www.php.net/manual/en/language.oop5.object-comparison.php

There seem to be a case when testing with "==" when "===" should be used to indentify objects can result in a overhead that could be avoided.

Not sure what really is the better option id==id or object===object. Maybe someone else can tell better.

Link to comment
Share on other sites

Not sure what really is the better option id==id or object===object. Maybe someone else can tell better.

I don't think it matters much here. The object===object is probably about the same thing as an id==id integer comparison, when it gets down into the low level code that it's written in. That's because object===object is comparing that two objects are the same instance, which translates to: do they occupy the same place in memory? (1234567==1234567)

But there is a good reason to use the id==id over object===object in ProcessWire:

$about1 = $pages->get('/about/');
$about2 = $pages->get('/about/');
echo $about1 === $about2 ? "Same Instance " : "Different Instance ";

This should output "Same Instance". But then try this:

$about1 = $pages->get('/about/');
$about1->set('title', 'About Test')->save(); // can be any page that is changed then saved
$about2 = $pages->get('/about/');
echo $about1 === $about2 ? "Same Instance " : "Different Instance ";

This should output "Difference Instance". Why? The entire memory cache is cleared when you save a page where one or more fields changed, or if you delete a page. It doesn't matter if it was $about1, or some other page, the memory cache is wiped. Since the code above kept it's own copy of the /about/ page before it was saved, there are now two copies of /about/ in memory: your copy and ProcessWire's copy.

For this reason, you may prefer to compare the 'id' property if the code you are working with is manipulating any pages or interacting with modules that do. But for most front-end situations, it doesn't matter.

  • Like 4
Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...