Created
October 17, 2016 13:28
-
-
Save jthatch/11e9e356bd60984779758734fed0434d to your computer and use it in GitHub Desktop.
A Laravel function to read two directories of pics, landscape and portrait, merging the pics so they fit together
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
/** | |
* | |
* getPics - Takes two directories of portrait and landscape pictures and merges them to create an ordered 2 column thumbnail gallery. | |
* | |
* Instead of reading from an API, it reads pics from the filesystem, caching the results in redis for self::$cache minutes | |
* There currently two pic sizes, "portrait" and "landscape", possibly more added in the future.. | |
* | |
* It will take into account the totals of each size, and spread the smaller sizes evenly across the resulting list in sets of 2. | |
* | |
* This behaviour is required to create a nice looking thumbnail list, | |
* where portrait images which take up a higher vertical hight are not interspersed with landscape pics on the same row, | |
* which creates undesirable empty space. | |
* | |
* E.G.: If there are 40 portrait pics and 10 landscape they will be placed like follows: (P = portrait, L = landscape) | |
* P P | |
* L L | |
* P P | |
* P P | |
* P P | |
* P P | |
* L L | |
* .. and so on | |
* With the Landscape pics spread evently across the length of the list, rather than grouped at the beginning | |
* | |
* | |
* Files are located here on the filesystem: | |
* app_path()/pics/{site}/{size}/large | |
* | |
* - jthatch 14/12/15 | |
* @param string $onlySize | |
* @param integer $columns | |
* @return array | |
*/ | |
private function getPics($onlySize = '', $columns = 2) | |
{ | |
$pics = Cache::remember("mobile.wallpaper.{$this->site->hash}", self::$cache, function() use ($onlySize, $columns) { | |
$pics = []; | |
// define our storage instance | |
$disk = Storage::disk('local'); | |
$path = "pics/{$this->site->hash}"; | |
//$sizes = []; | |
//$sizes = ['portrait', 'landscape']; | |
$sizes = $disk->directories($path); | |
if (empty($sizes)) { | |
Log::error("No pics found in {$path}"); | |
} | |
// just take the last path | |
array_walk($sizes, function (&$size) { | |
if (stristr($size, DIRECTORY_SEPARATOR)) { | |
$paths = explode(DIRECTORY_SEPARATOR, $size); | |
$size = array_pop($paths); | |
} | |
}); | |
// if we just want a single set of pics, e.g. only portrait, we call the function with the size we want | |
if ($onlySize && in_array($onlySize, $sizes)) | |
$sizes = [$onlySize]; | |
/** | |
* Iterate over the sizes. | |
* Read the images from the filesystem and create an array of image objects stored in $pics[$size] | |
*/ | |
foreach ($sizes as $size) { | |
// define our path | |
$path = "pics/{$this->site->hash}/{$size}/large"; | |
try { | |
$files = $disk->allFiles($path); | |
if (empty ($files)) { | |
Log::error("No pics found in {$path}"); | |
} else { | |
// Only create entry if there are any pics | |
$pics[$size] = []; | |
foreach ($files as $f) { | |
$parts = pathinfo($f); | |
$filename = $parts['filename']; // 123 | |
// initializing a PHP object based on array is faster than invoking a new stdClass().. | |
$pics[$size][] = (object)[ | |
'id' => $filename, | |
'large' => '/' . $f, | |
'thumb' => "/" . str_replace(["/large/", "."], ["/thumbs/", "_thumb."], $f), | |
'url' => !!$this->site->dynamic_videos_serve_static ? url("pics/{$filename}.html") : url("wallpapers/{$filename}"), | |
'size' => $size | |
]; | |
} | |
} | |
} catch (\ErrorException $e) { | |
Log::error("Error accessing {$path}: {$e->getMessage()}"); | |
} | |
} | |
/** | |
* If we have multiple sizes then let's distrubute the sizes according to our rules defined above. | |
*/ | |
if (sizeof($pics) > 1) { | |
// store our incrementing counters | |
$counters = []; | |
// store the biggest arrays first | |
$pic_order = []; | |
// store the total pics | |
$total_pics = 0; | |
foreach (array_keys($pics) as $size) { | |
$counters[$size] = (object)[ | |
'total' => sizeof($pics[$size]) | |
]; | |
// use total is the array index referencing the size so we can sort by biggest below | |
$pic_order[$counters[$size]->total] = $size; | |
$total_pics += $counters[$size]->total; | |
}; | |
// start with the biggest first and use this as our "base" | |
rsort($pic_order); | |
// the biggest size will be the first element of pic_order | |
$biggest = $pic_order[0]; | |
/* Now that we know the biggest size, find the frequency that these sizes should be | |
* inserted into the list, represented by every xth iteration | |
*/ | |
foreach ($pic_order as $size) { | |
$counters[$size]->frequency = ceil( | |
$counters[$biggest]->total / | |
($counters[$size]->total > 0 ? $counters[$size]->total : 1) | |
); | |
} | |
/** | |
* Frequency will store our pics in an order manner | |
*/ | |
$frequency = []; | |
$counter = 0; | |
// we do this in batches of $columns | |
for ($i = 0; $i < $total_pics; $i += $columns) { | |
// using the pic order lets iterate over our counters | |
foreach ($pic_order as $key) { | |
$size = &$counters[$key]; | |
if ($counter % $size->frequency == 0) { | |
if ($size->total > 0) { | |
// to avoid the ghost array if we get down to 1 left we don't want to pull 2 by mistake | |
$ceil = min($columns, $size->total); | |
for ($j = 0; $j < $ceil; $j++) { | |
$frequency[] = array_pop($pics[$key]); | |
//$frequency[] = $key; | |
} | |
$size->total -= $columns; | |
} | |
} | |
} | |
$counter++; | |
} | |
return $frequency; | |
} // if we have just one that's easy | |
else if (sizeof($pics) == 1) { | |
return array_pop($pics); | |
} else { | |
return []; | |
} | |
}); | |
return $pics; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment