Skip to main content

This site requires you to update your browser. Your browsing experience maybe affected by not having the most up to date version.

We've moved the forum!

Please use forum.silverstripe.org for any new questions (announcement).
The forum archive will stick around, but will be read only.

You can also use our Slack channel or StackOverflow to ask for help.
Check out our community overview for more options to contribute.

DataObjectManager Module /

Discuss the DataObjectManager module, and the related ImageGallery module.

Moderators: martimiz, UncleCheese, Sean, Ed, biapar, Willr, Ingo, swaiba

Newbie: Retrieving first of 'has many' image items in frontend.


Go to End


6 Posts   2181 Views

Avatar
timwjohn

Community Member, 98 Posts

1 October 2009 at 2:39am

Hi all,

Firstly, I'd like to say how exciting SilverStripe is in its possibilities and can't wait to become a fully fledged developer & contributor to the community.

My understanding of PHP is limited, and maybe that is why I'm finding it hard to make the penny drop with SilverStripe, but I'm an advanced AS3 developer, so I know when it all falls into place in my head I can build some momentum (and websites). I'm just going round in silly logical circles at the mo.

I'm a little stuck with a very simple task. Just wanted to know the best way to do it.

It's for my portfolio website. I've got Page subclasses PortfolioList & PortfolioItem and a DataObject subclass PortfolioImage. PorfolioItem is a child of PortfolioList and PortfolioItem 'has many' PortfolioImages, which I'm successfully managing in the CMS using ImageDataObjectManager, which I admit is the only reason I'm posting in this particular forum.

What I would like to do in the PortfolioList template is output each PortfolioItem in a box with its first PortfolioImage as a thumbnail of a certain width.

Here're my PortfolioItem & PortfolioioImage classes:

class PortfolioItem extends Page
{
	static $db = array(
		'categoryWeb' => 'Boolean',
		'categoryFlash' => 'Boolean'
	);
	
	static $has_one = array(
	);
	
	static $has_many = array(
		'Images' => 'PortfolioImage'
	);
	
	function getCMSFields()
	{
		$fields = parent::getCMSFields();
		
		$manager = new ImageDataObjectManager(
			$this, // Controller
			'Images', // Source name
			'PortfolioImage', // Source class
			'Image', // File name on DataObject
			array('ImageTitle' => 'ImageTitle'), // Headings
			'getCMSFields_forPopup' // Detail fields (function name or FieldSet object)
			// Filter clause
			// Sort clause
			// Join clause
		);
		
		$fields->addFieldToTab('Root.Content.Main', new CheckboxField($name = 'category_web', $title = 'Web'), 'Content');
		$fields->addFieldToTab('Root.Content.Main', new CheckboxField($name = 'category_flash', $title = 'Flash'), 'Content');
		$fields->addFieldToTab('Root.Content.Images', $manager);
			
		return $fields;
	}
}

class PortfolioImage extends Image
{
	static $db = array(
		'ImageTitle' => 'Text'
	);
	
	static $has_one = array(
		'Image' => 'Image',
		'PortfolioItem' => 'PortfolioItem'
	);
	
	public function getCMSFields_forPopup()
	{
		return new FieldSet(
			new TextField('ImageTitle', 'Image Title')
		);
	}
}

This is what I've got so far in the PortfolioList template. For now I'm just outputting the PortfolioImage URLs to see that it's working, and it is:

<% control Children %>
	<div class="box box-portfolio">
		<div class="box-element box-tl"></div><div class="box-element box-tc"></div><div class="box-element box-tr"></div>
		<div class="box-element box-middle">
			<% control Images %><p>$Link</p><% end_control %>
  			<!--<a href="$Link"><img src="" alt="Screenshot" /></a>-->
  			<h2><a href="$Link">$Title</a></h2>
		</div>
		<div class="box-element box-bl"></div><div class="box-element box-bc"></div><div class="box-element box-br"></div>
	</div><!--/box-portfolio-->
<% end_control %>

So, would I create a function in PortfolioItem to get the first image? Also would I create a function in PortfolioImage generate a set thumbnail size? Or is there a better way?

Would sopmeone be able to provide some real code examples of how I could achieve this, with some basic explanation? Maybe this will help me understand the structure of SilverStripe just that bit more and I can move on to the next notch.

My sincere thanks and apologies!

Tim

Avatar
UncleCheese

Forum Moderator, 4102 Posts

1 October 2009 at 3:36am

First, if you're using ImageDOM, you should be managing DataObject subclasses, not an Image subclass. If you're going to manage an Image class directly, you can use ImageAssetManager, but since your PortfolioImage object has a custom field (ImageTitle) you need to do it as a dataobject. If you think about it, it doesn't make too much sense to have the self-referential relation here:

static $has_one = array(
'Image' => 'Image',
'PortfolioItem' => 'PortfolioItem'
);

You have an image that has_one image. Sometimes that's what you want, but not in this case.

class PortfolioImage extends DataObject 
{ 
   static $db = array( 
      'ImageTitle' => 'Text' 
   ); 
    
   static $has_one = array( 
      'PortfolioItem' => 'PortfolioItem',
      'Image' => 'Image'
   ); 
    
   public function getCMSFields() 
   { 
      return new FieldSet( 
         new TextField('ImageTitle', 'Image Title') 
      ); 
   } 
}

If you're on the absolute latest SVN checkout of DOM (as of this morning) you can clean up your function using the intelligent constructor feature

      $manager = new ImageDataObjectManager($this);

but otherwise, use

      $manager = new ImageDataObjectManager( 
         $this, // Controller 
         'Images', // Source name 
         'PortfolioImage', // Source class 
         'Image', // File name on DataObject 
         array('ImageTitle' => 'ImageTitle'), // Headings 
      ); 

And on your template, first image and thumbnail:

 <div class="box-element box-middle"> 
         <% control Images.First %>
<% control Image %>
$CroppedImage(100,100)
         <% end_control %> 
<h2>$ImageTitle</h2>
      </div> 

Avatar
timwjohn

Community Member, 98 Posts

1 October 2009 at 6:35am

Thanks UncleCheese! I felt myself levelling up as I read your reply. Of course extending DataObject makes more sense anyway, and now my popups are popping automatically after all the items have uploaded.

Can you think why the Drag & Drop Reordering option has now disappeared?!

Avatar
UncleCheese

Forum Moderator, 4102 Posts

1 October 2009 at 7:09am

Ahh... right. You need to add that in manually. It just so happens that DOM makes all File descendants sortable on its own, but in the interest of keeping your tables uncluttered, you have to opt into sorting in your _config.php.

SortableDataObject::add_sortable_class("MyDataObject");

For multiple objects, you can pass an array of names to add_sortable_classes();

Glad it's working for you. Happy to help out.

Avatar
timwjohn

Community Member, 98 Posts

2 October 2009 at 3:48am

Thanks UncleCheese! I do remember you mentioning that in your YouTube video. Silly me.

Avatar
vancouverWill

Community Member, 121 Posts

14 October 2009 at 1:36pm

Hi Guys

Once again I'm really impressed by SilverStripe. It works great the way the image dataobjectoperator can be used within the popup. Really intuitive it is. One thing I noticed is that once the images are uploaded there is no way that I can see of simply deleting the images already there. Wondering how you guys approaced this. I was thinking of adding a ConfirmedFormAction button and putting a delete action on it, that works fine to delete the file but then you are left in an empty popup box. Thanks again

Will