This article provides a quick tutorial on how to write your own Pinterest like Image Gallery using the jQuery Wookmark Plugin and Symfony2.
Assuming you already created a Symfony2 Bundle and image files are stored on disk, below are the steps to follow for writing a new Controller and Twig template.
WookmarkController (/Controller/WookmarkController.php)
NOTE: Requires the Finder component for listing image files, and the PHP GD extension.
// Namespace and bundle specific code goes here // Make use of Symfony2 FileSystem functionality. use SymfonyComponentFilesystemFilesystem; use SymfonyComponentFilesystemExceptionIOException; // Make use of the Symfony2 Finder use SymfonyComponentFinderFinder; // Use Request object use SymfonyComponentHttpFoundationRequest; /** * Provides Wookmark UI and JSON list. * Requires the Finder component: http://symfony.com/doc/2.0/components/using_components.html */ class WookmarkController extends Controller { // Image directory const IMAGE_DIRECTORY = "/path/to/images/on/disk"; // File name pattern: http://api.symfony.com/2.0/Symfony/Component/Finder/Finder.html#files() const FILENAME_PATTERN = "*.jpg"; // Default page size const PAGE_SIZE = 20; /** * Generates the user interface. */ public function indexAction() { // Render template return $this->render( 'BridgemanContemporaryBundle:Wookmark:index.html.twig' ); } /** * Returns a list of images, and their properties. * NOTE: Requires php-gd, for fetching image dimenstions! */ public function listAction( Request $request ) { // Prepare finder $finder = new Finder(); // Load image file names, from the configured directory... $finder->in( self::IMAGE_DIRECTORY ) // ...and the specified pattern ->name( self::FILENAME_PATTERN ) // ...sorted by name ->sortByName() // ...non-recursive ->depth( '== 0' ); // Prepare paging $start = ( $request->get( 'page' ) - 1 ) * self::PAGE_SIZE; $end = $start + self::PAGE_SIZE; // Prepare the array of images $images = array(); $item = 0; // Paging index foreach ( $finder as $file ) { // Verify page if ( $item >= $start && $item < $end ) { // Get image dimensions list( $width, $height ) = @getimagesize( $file->getRealpath() ); // Push each image $images[] = array( // File properties "file" => $file->getFilename() ,"size" => $file->getSize() ,"last_change_time" => $file->getCTime() // Image properties ,"width" => $width ,"height" => $height ); } // Exit loop, if already parsed all images for the current page if ( $item == $end ) { break; } $item++; } // Render template return $this->render( 'BridgemanContemporaryBundle:Wookmark:list.html.twig' ,array( // Image array "images" => $images ) ); } }
List action template (/Resources/views/Wookmark/list.html.twig)
{{ images|json_encode|raw }}
Index action template (/Resources/views/Wookmark/index.html.twig)
Wookmark Image Galley /* Body style */ body { margin: 0px; padding: 0px; background-color: black; } /* Image container style */ #container { width: 900px; margin-left: auto; margin-right: auto; } /* Image box style */ #container img { cursor: auto; }
// From Wookmark Examples var handler = null; var page = 1; var isLoading = false; var apiURL = '/wookmark/list'; var imageURL = 'IMAGE_URL'; var imageWidth = 200; // Prepare layout options. var options = { autoResize: true, // This will auto-update the layout when the browser window is resized. offset: 20, // Optional, the distance between grid items container: $( '#container' ), // Optional, used for some extra CSS styling itemWidth: 210 // Optional, the width of a grid item }; /** * When scrolled all the way to the bottom, add more tiles. */ function onScroll(event) { // Only check when we're not still waiting for data. if(!isLoading) { // Check if we're within 100 pixels of the bottom edge of the broser window. var closeToBottom = ($(window).scrollTop() + $(window).height() > $(document).height() - 100); if(closeToBottom) { loadData(); } } }; /** * Refreshes the layout. */ function applyLayout() { // Clear our previous layout handler. if(handler) handler.wookmarkClear(); // Create a new layout handler. handler = $('#tiles li'); handler.wookmark(options); }; /** * Loads data from the API. */ function loadData() { isLoading = true; var data = { page: page }; $.ajax({ url: apiURL, dataType: 'json', data: data, // Page parameter to make sure we load new data success: onLoadData }); }; /** * Receives data from the API, creates HTML for images and updates the layout */ function onLoadData(data) { isLoading = false; // Increment page index for future calls. page++; // Create HTML for the images. var html = ''; var i=0, length=data.length, image; var lastModifyDate; for(; i