Jump to content

duplicate foreach in PHP


Peter Knight
 Share

Recommended Posts

I have a complicated, nested IF statement which I'm trying to clean up.

Right now it works and only displays if 

  • user=superuser
  • a matrix item of type 'related_product' exists

My main issue is I have two of the follwoing

foreach($page->related_products as $matrix_item) {}

If I delete the first one, the page throws a server error
if I delete the second one and it's opening and closing curly bracket, only a single related product displays. In reality there are 4. 

I shouldn't need two of these in my code but I'm clearly micing up my end curly brackets or something

 

<!-- Gets the related products at the bottom of the page -->
<?php

		// For each related_products in Matrix field set a variable called $matrix_item
		foreach($page->related_products as $matrix_item) {}

		// START IF statement: superuser and related product
		if (($user->hasRole("superuser")) && ($matrix_item->type == 'related_product')) 
		{
			echo
			"
			<div class='uk-section uk-section-muted section-related'>
				<div class='uk-container'>
				<h3>Related Products</h3>
				<div class='uk-grid-match uk-child-width-1-4@s' uk-grid>
			";
			
			// For each related_products in Matrix field set a variable called $matrix_item
			foreach($page->related_products as $matrix_item)
			{
				// For every child page of the related_random page, output a single random one based on the product template...
				foreach ($matrix_item->related_random->children('template=product, sort=random, limit=1') as $prod)
				{

				// output this
					echo "
						<div>
							<div class='rel-prod-ov-wrapper'>
								<a href='$prod->url'>
								<img src='{$prod->images->first()->url}' class='rel-prod-preview'>
								</a>

								<div class='rel-prod-ov-text'>
									<span class='rel-preview-title'><strong>Part No:</strong> {$prod->title}</span>
									<br/>
									{$prod->prod_summary}

									<a href='$prod->url' class='uk-icon-button  rel-icon-linky' uk-icon='icon: chevron-right'></a>
								</div>
							</div>	
						</div>
					";
				}
			}
		echo
			"
					</div>
				</div>
			</div>
			
			";
		}
		// END IF statement: superuser and related product

?>
			
			
			

 

related_productsjpg.jpg

Link to comment
Share on other sites

This code seems to render a repeater matrix, are you using field render functionality? Like echo $page->render->matrix_field which uses /site/templates/fields/matrix_field.php? If so, you can divide your code into multiple files for each matrix type and put those under /site/templates/fields/matrix_field/type1.php and so on.

https://processwire.com/blog/posts/more-repeaters-repeater-matrix-and-new-field-rendering/#processwire-3.0.5-introduces-field-rendering-with-template-files

There's also wireRenderFile() to render a template file with given variables and return string output. I use it a lot in my templates.

https://processwire.com/blog/posts/processwire-2.5.2/#new-wirerenderfile-and-wireincludefile-functions

I tried to clean up your code. Some remarks:

  • If you know there's only one page in a wirearray, you can get it with $arr->first
  • Use if(! $some_condition) return; to reduce nesting
  • You can open and close <?php ?> tags to give yourself some flexibility, so you wont have to put your HTML inside PHP strings. Anything you put between php tags will be echoed. 
<?php namespace ProcessWire;

// dont show if user is not superuser
if (!$user->hasRole('superuser')) return;

// dont display partial markup if there are no related products
if(!$page->related_products->count) return;

// used for picking a random product from related products
$randomSelector = 'template=product, sort=random, limit=1';



?>
<div class='uk-section uk-section-muted section-related'>
    <div class='uk-container'>
        <h3>Related Products</h3>
        <div class='uk-grid-match uk-child-width-1-4@s' uk-grid>
            <?php foreach ($page->related_products as $matrix_item): ?>
                <?php $randomProd = $matrix_item->related_random->children($randomSelector)->first; ?>
                <div class='rel-prod-ov-wrapper'>
                    <a href='<?= $randomProd->url ?>'>
                        <img src='<?= $randomProd->images->first()->url ?>' class='rel-prod-preview'>
                    </a>

                    <div class='rel-prod-ov-text'>
                        <span class='rel-preview-title'><strong>Part No:</strong> <?= $randomProd->title ?></span><br/>
                        <?= $randomProd->prod_summary ?>
                        <a href='<?= $randomProd->url ?>' class='uk-icon-button  rel-icon-linky' uk-icon='icon: chevron-right'>
                            <!-- THERE SHOULD BE SOME TEXT HERE -->
                        </a>
                    </div>
                </div>
            <?php endforeach; ?>
        </div>
    </div>
</div>

Not sure if this works, there are some parts I couldnt make sense, but it should give you some ideas. If you give more info, I might be able to help you better

  • Like 2
Link to comment
Share on other sites

33 minutes ago, Peter Knight said:

foreach($page->related_products as $matrix_item) {}

If I delete the first one, the page throws a server error
if I delete the second one and it's opening and closing curly bracket, only a single related product displays. In reality there are 4. 

The first loop assigns each item in $page->related_products to $matrix_item, so you have one $matrix_item after the first loop.

When you delete the first empty loop you will have no $matrix_item anymore in your if statement.

When you delete the second one then you will have only one $matrix_item, from your first empty loop.

  • Like 3
Link to comment
Share on other sites

I would also like to point out what @abdus also did in his example: I try to avoid expressions like these:

<?php
foreach($page->related_products as $matrix_item) {}
foreach ($matrix_item->related_random->children('template=product, sort=random, limit=1') as $prod) {}

instead:

<?php
$related_products = $page->related_products;
foreach(related_products as $matrix_item) {}
$random_children = $matrix_item->related_random->children('template=product, sort=random, limit=1');
foreach ($random_children as $prod) {}

Often easier to read but most importantly one can inject bd($random_children) /Tracy/ or error_log(print_r($random_children,true)); into the code to debug the result sets.

BTW, I also use the "style" @abdus presented. PHP is A templating engine itself and that is how a native PHP template looks.

  • Like 1
Link to comment
Share on other sites

Thanks guys..

@abdus I'm not familiar with a lot of your method but I'm looking forward to reading up on them and improving my PHP. I'm not familiar with return for example.

 In the short term I might break down my single PHP block into smaller more manageable blocks and rework it a little. 

I did try using your example to test it and I got a server error but aside from that am I correct in thinking that the actual Related Product heading isn't based on any conditional. 

@szabesz and @fbg13 thanks for the tips. 

  • Like 1
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...