Jump to content

AJAX and image sizing


muzzer
 Share

Recommended Posts

I'm building my first PW site (lovin this system) and working on a custom shopping cart at present. But I'm having a problem getting image resizing to work using AJAX. I have a function which is called in different circumstances both normally and via AJAX

function generateCart( $page, $pages, $config )
{
  $purchases = $pages->get( '/wcart/sprees/' . session_id() . '/' )->find( 'template=wcart-product-page' );
  foreach( $purchases as $purchase )
  {
   $product = $pages->get( $purchase->wcart_product_id );
   $productImage = $product->wcart_image->size( 80, 60 );
   ....
  }
}

Calling from AJAX fails with the error:

WireException: Method Pageimages::size does not exist or is not callable in this context [C:\wamp\www\spv\wire\core\Wire.php:232]

The function call normally is :

echo generateCart( $page, $pages, $config );

That's fine, works as you would expect. But from ajax:

if(wire('config')->ajax)
  echo generateCart( wire( 'page' ), wire( 'pages' ), wire( 'config' ));

and it throws the error. What's going on, is something not visible here that needs to be?

In case you're wondering 'wcart_image' is a field of type image.

Link to comment
Share on other sites

WireException: Method Pageimages::size does not exist or is not callable in this context

Looks like you need to threat the image(s) as array adding a ->first()->size()

$productImage = $product->wcart_image->first()->size( 80, 60 );
Link to comment
Share on other sites

Looks like you need to threat the image(s) as array adding a ->first()->size()
$productImage = $product->wcart_image->first()->size( 80, 60 );

Thanks but doing so produces:

Fatal error: Call to a member function size() on a non-object.

$product->wcart_image is not an array according to is_array($product->wcart_image)

Link to comment
Share on other sites

Soma is right in his suggestion, but chances are that your "non-object" error came because you tried to do it on a page with no images. Thus, first() returned null and you tried to call size() on a non-object. So you'd want to do something like this instead:

$productImage = $product->wcart_image->first();
if($productImage) $productImage = $productImage->size(80,60); 

or this

if(count($product->wcart_image)) {
 $productImage = $product->wcart_image->first()->size(80,60); 
}
Link to comment
Share on other sites

Thanks Ryan. Tried your suggestion, resulting in:

E_USER_ERROR: Exception: Method Pageimage::first does not exist or is not callable in this context

The generateCart function is called on a "show cart" page to generate the shopping cart table. There is definately an image for the product. It's called again (but with parameters passed in wire('var') format) from ajax when the number of items is adjusted (from an input field in the cart table), so nothing has changed at this point (images etc) other than the number of items in the cart.

Essentially what it appears to me is there is something missing when the function is called via ajax as it's exactly the same code in every respect other than the call method. It's doing my head in!

Link to comment
Share on other sites

Further:

function generateCart( $page, $pages, $config )
{
  $purchases = $pages->get( '/wcart/sprees/' . session_id() . '/' )->find( 'template=wcart-product-page' );
  foreach( $purchases as $purchase )
  {
     $product = $pages->get( $purchase->wcart_product_id );
     debug( $product->wcart_image );
     $productImage = $product->wcart_image;
     debug($productImage);
     if($productImage)
        $productImage = $productImage->size(80,60);
     ....
  }
}

The two debug lines output a string 'bioflow-dog-collars.jpg' (not an array). Output of both is the same (as you would expect).

Changing

$productImage = $product->wcart_image;

to

$productImage = $product->wcart_image->first();

produces the error.

Link to comment
Share on other sites

Hey Soma, sorry, I completely didn't see your reply;

It would not be a normal php array but a WireArray with image objects.

What does this mean in practice exactly? I'm still trying to get my head around this. How would it be accessed (compared to a normal php array)?

Where is your function located? What is the setup exactly there?

Edit: And from where are you doing the function call?

The function is located in /site/wdnzScripts/wcart.inc.php. When called from ajax this file is called directly and the function call is

if(wire('config')->ajax)
  echo generateCart( wire( 'page' ), wire( 'pages' ), wire( 'config' ));

and is located directly above the function. When called normally the function call is in a template:

include_once( $config->paths->root . 'site/wdnzScripts/wcart.inc.php' );
echo generateCart( $page, $pages, $config );

I'm obviously missing something obvious here. Both you and Ryan say it should be an array so thats as good as gospel for me, it should be an array. But whatever way I look at it it appears to be a string!

Link to comment
Share on other sites

I just ran a little test with latest PW and I have no problems requesting a special "/ajax/" page with a template with more or less your example code. Returning a scaled image string, doesn't matter from where. So I can't reproduce it.

Link to comment
Share on other sites

I'm still not sure when reading your setup and where is what. It can be confusing lol :D

Just think of it as a special type of array that has certain methods depending on type etc.

Usually in templates, $page->image as the name would suggest it is a single image field (field setting to max=1), so it would be directly the one image object.

If you have a "images" field, and set it to max=0, so no limit, the field will be an array in the templates files and you have to iterate through them or directly access it by some of these methods WireArray's have. So ->first() or ->eq(2) would give you the image object.

Now in certain cases this array, single object behaviour is only on template and admin level. Technically it's always an WireArray, but the system has a output formatting that will take care for that. So if you turn of outputformatting you would always have to use ->first() to access the one item in the array. So it's possible that this is the case, but it's obviously not, or I'm still not sure what causes the problem in your case.

Link to comment
Share on other sites

The function is located in /site/wdnzScripts/wcart.inc.php. When called from ajax this file is called directly and the function call is

if(wire('config')->ajax)
  echo generateCart( wire( 'page' ), wire( 'pages' ), wire( 'config' ));

Call it directly? You can't put a script in the /site/yourdir/.. folder an call it directly via ajax, that will give access denied by htaccess. The function you write, that is not an ajax call, an ajax call is made via javascript. So can't say for sure I understand.

include_once( $config->paths->root . 'site/wdnzScripts/wcart.inc.php' );
echo generateCart( $page, $pages, $config );

I'm obviously missing something obvious here. Both you and Ryan say it should be an array so thats as good as gospel for me, it should be an array. But whatever way I look at it it appears to be a string!

But I'm maybe also missing the obvious or still don't get your code flow, for example where and how do you do the ajax request? And where are you seeing the error?

Link to comment
Share on other sites

Soma, thanks for your time on this.

Sorry I seems to have confused you as to how it's all set up, so heres a fuller version of the code:

ajax call:


  $('.numberAdjustment').live('change', function(){
     var $this = $(this)
     var btnText = $(this).html();
     var inputVal = $(this).val();
     var inputId = $(this).attr('name');
     var serial = "inputId=" + inputId + "&inputVal=" + inputVal;
     $.ajax({
        url: location.protocol + '//' + location.host + '/site/wdnzScripts/wcart.inc.php',
        type: "post",
        data: serial,
        success:
           function(responseText){  
              $('#cart').fadeOut(500, function() {
                 $('#cart').css('display', 'none');
                 $('#cart').html(responseText);
                 $('#cart').fadeIn(500);
                 showNotif('<p>Your cart contents have been updated.</p>', $this, btnText );
              });
           },
        error: function(){
           alert("Oops, there was an error adjusting the cart contents");
        }
     });
  });

that simply detects when the input with class 'numberAdjust' in the cart table is altered and calls out troublesome script, which updates the number of the product in the cart and returns the updated cart table HTML. It then fades the old cart table out and fades the updated version in. No problems there.

The script /site/wdnzScripts/wcart.inc.php:

<?php 

include_once( $_SERVER['DOCUMENT_ROOT'] . $_SERVER['HTTP_HOST'] . '/index.php' ); // bootstrap PW

// ajax calls
if(wire('config')->ajax)
{

   // set number of units of a product in the cart
   if( isset( $_POST['inputId'] ) AND isset( $_POST['inputVal'] ))
   {
      $id = str_replace( 'units_id', '', $_POST['inputId'] );
      alterProductPurchase( wire( 'pages' ), wire( 'input' ), wire( 'config' ), $id );
      echo generateCart( wire( 'pages' ), wire( 'config' ));
   }

   return;
}


// alter number of units for an existing product in  the cart. Called from the units input in the cart table
function alterProductPurchase( $pages, $input, $config, $id )
{
  // check if the page with this exact product already exists, if so add to it, else create
   $purchase = $pages->get( '/wcart/sprees/' . session_id() . '/' )->find( 'template=wcart-product-page,id=' . $id    );
   if( $purchase[0] 
)    {
       if( $input->post['inputVal'] == 0 )
       {
           // delete product from cart
           $pages->delete( $purchase[0] );
       }
       else
       {
           // exact product exists for this customer, so add the new purchase's units 
           $purchase = $purchase[0]; // get first record returned
           $purchase->wcart_product_units = $input->post['inputVal'];
            $purchase->setOutputFormatting( false );
            $purchase->save();
        }
    }
}



function generateCart( $pages, $config )
{
   $purchases = $pages->get( '/wcart/sprees/' . session_id() . '/' )->find( 'template=wcart-product-page' );
   $cartHtml = '<table id="cart" class="table table-striped table-bordered table-hover"><thead><tr><th>id</th><th>title</th><th>colour</th><th>size</th><th>number</th><th></th><th></th></tr></thead><tbody>';
   foreach( $purchases as $purchase )
   {
       $product = $pages->get( $purchase->wcart_product_id );
       $productImage = $product->wcart_image;
       debug( 'prior to image resize' );
       if($productImage)
           $productImage = $productImage->size(80,60);
       //THE ERROR OUTPUTS BETWEEN THESE TO DEBUG CALLS ('debug' is a chrome/lagger function)
        debug( 'after image resize' );

       $cartHtml .= '<tr>';
       $cartHtml .= '<td><a href="' . $product->url . '"><img src="' . $productImage->url . '" width="80" alt="" /></a></td>';
       $cartHtml .= '<td>' . $product->title . '</td>';
       $cartHtml .= '<td>' . $purchase->wcart_product_colour . '</td>';
       $cartHtml .= '<td>' . $purchase->wcart_product_size . '</td>';
       $cartHtml .= '<td><input type="text"class="numberAdjustment input-mini" name="units_id' . $purchase->id . '" value="' . $purchase->wcart_product_units . '"></td>';
       $cartHtml .= '<td><button class="removeLink btn btn-mini btn-danger" id="remove_id' . $purchase->id . '"type="button">Remove</button></td>';
       $cartHtml .= '<td></td></tr>';
   }
   $cartHtml .= '</tbody></table>';
   return $cartHtml;
}
?>

Note: If I remove the image resizing lines and the script/ajax works beautifully.

Link to comment
Share on other sites

I see, still even if I rebuild yours and also include the bootstrap, I can call it directly via a ajax jquery script. And I can reproduce that what we first answered. Even if it's a single image field you have to call it with $p->image->first()->sie

Now looking again at the error code "Pageimages::size"

That's exactly why me and Ryan suggested to try the ->first() method. And my tests show exactly this behaviour. Works fine here so can't really help and don't see anything wrong that sticks out.

I have a /site/ajax/ajax.inc.php

include_once( $_SERVER['DOCUMENT_ROOT'] . "/index.php");

function generateCart( $pages, $config ) {
 $p = $pages->get("/about/");
 $img = $p->image->first()->size(100,120);
 return $img->url;
}

if(wire('config')->ajax){
 echo generateCart( wire("pages"), wire("config") );
}

And when I call it directly from a pw page

<script>

$.ajax({
 url: "/site/ajax/ajax.inc.php",
 success: function(data){
   $('#bodycopy').html(data);
 }
});

</script>

I get this back

/site/assets/files/1001/pastedgraphic-1.100x120.png

And the image field "image" is limited to 1 max. So in a normal call in a PW page you only would do $page->image->size(100,0)->url

Link to comment
Share on other sites

The only thing I see now to reproduce your fatal error ist writin ->first instead of ->first()

This has come up so often it was clear from the beginning it has to do with it being and "Pageimages" WireArray instead of a single object of type "Pageimage". That's most likely when you directly request it and include the PW index.php.

BTW do you also bootstrap that when doing normal function call when included? Because it's not necessary on a PW template file.

SO you most likely have to also include a check for using first() or not depending if it's an ajax request or not.

Link to comment
Share on other sites

SO you most likely have to also include a check for using first() or not depending if it's an ajax request or not.

You are right! I've altered it to read:

  if(wire('config')->ajax)
     $productImage = $product->wcart_image->first()->size(80,60);
  else
     $productImage = $product->wcart_image->size(80,60);

and it's all go :)

Still, I'm not sure I understand why this is so. Can someone explain exactly how wire('page') is different structurally to $page. This is one aspect of PW that has confused me from day dot.

Thank you Soma for your awesome assistance. Much appreciated.

Link to comment
Share on other sites

  • 4 years later...

Bumping up this really old thread because I got into this issue, calling any resize functions on a single image field throws the exception mentioned previously, as if the object I am calling the resize function from, is of type Pageimages.

Should this be the expected behaviour? I think it's really non intuitive that it happens only on AJAX calls.  Maybe a server specs specific issue?

Thanks for further clarification!

Link to comment
Share on other sites

@elabx, the unformatted value of an images field is always a WireArray. Having the value be a Pageimage for a single image field is the result of output formatting.

I think what Soma is saying above is that output formatting is off when you bootstrap the PW index.php. So the issue doesn't occur because of an AJAX call, it occurs because of the bootstrapping.

So solutions would be either:

  • Just be aware that output formatting is off when bootstrapping PW and adjust your code accordingly
  • In your AJAX call, load a normal PW page rather than a PHP file that bootstraps index.php
  • Like 2
Link to comment
Share on other sites

  • 2 weeks later...

I'm sorry I didn't read this correctly before, but  to clarify I wasn't bootstraping the PW installation, it was a plain AJAX call to the same URL.

Why I came back? I had a "similar" issue where I tried pulling some content via AJAX and when trying to use $user->isLoggedin(), the result always returned true. I moved that code to a custom module (and called $modules->get("CustomModule")->someFunction() ) , and it all worked just fine. Would this issue be related to output formatting too?

Thanks for further help!

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...