Skip to content
David Drdek edited this page Dec 7, 2017 · 3 revisions

WIP payload utils (asi nekam do utils/payload.php)

// aplikuje funkci $fn na vsechny prvky pole $source
function map(array $source, $fn)
{
	return array_map($fn, $source);
}

// odebere hodnoty z pole co jsou NULL, FALSE, 0, '', ...
function filter($source, $fn = null)
{
	if ($fn) {
		return array_filter($source, $fn);
	}

	return array_filter($source);
}

/*
	valKeys([ 'a', 'b', 'c' ]); //=> [ 'a' =>, 'b' => 'b', 'c' => 'c' ]
*/
// hodi se, kdyz je pole IDček, ze ktereho chceme mapovani IDček na něco jinyho
function valKeys(array $data)
{
	$result = [];
	foreach ($data as $val) {
		$result[$val] = $val;
	}

	return $result;
}

/*
	$item = [ 'foo' => 'bar' ];
	pick('foo', $item); //=> 'bar'
*/
// z objektu, nebo pole vybere vec v klici
function pick(string $key, $source, $defaultValue = null)
{
	if (is_array($source)) {
		return $source[$key] ?? $defaultValue;
	} elseif (is_object($source)) {
		return $source->{$key} ?? $defaultValue;
	}

	return $defaultValue;
}

/*
	$a = [ 'id' => 1 ];
	$b = [ 'id' => 2 ];
	$c = [ 'id' => 3 ];
	$items = [ $a, $b, $c ];
	mapPick('id', $items); // => [ 1, 2, 3 ]
*/
// pick for arrays of items
function mapPick(string $key, array $source, $defaultValue = null)
{
	return map($source, function ($item) use ($key, $defaultValue) {
		return pick($key, $item, $defaultValue);
	});
}

// pak se daji psat takove zkratky
function mapId(array $source, $defaultValue = null)
{
	return mapPick('id', $source, $defaultValue);
}

// na $source se postupne aplikuji vsechny funkce z pole v druhem argumentu
// pipe('text', [ 'a', 'b', 'c' ]) dela to same jako c(b(a('text')))
function pipe($source, array $pipeline)
{
	$result = $source;

	foreach ($pipeline as $fn) {
		$result = call_user_func($fn, $result);
	}

	return $result;
}

// resi pripady, kdyz je v obsahu <p>[shortcode]</p>, ale nechceme aby vysledek shortcode byl uvnitr odstavce
function fixShortcodesInParagraphs(string $content)
{
	$array = [
		'<p>[' => '[',
		']</p>' => ']',
		']<br />' => ']',
	];
	$content = strtr($content, $array);

	return $content;
}

// automaticky prida ten filtr na reseni shortcodu pri filteru the_content
add_filter('the_content', 'fixShortcodesInParagraphs');

// zkratka

function applyContentFilter(string $str)
{
	return apply_filters('the_content', $str);
}

// Aby permalinky fungovaly na jine domene
function clearHostUrl(string $url)
{
	$serverUrl = (isset($_SERVER['HTTPS']) ? 'https' : 'http')."://$_SERVER[HTTP_HOST]";
	if (Nette\Utils\Strings::startsWith($url, $serverUrl)) {
		return substr($url, strlen($serverUrl));
	}

	try {
		$Url = new Nette\Http\Url($url);

		return $Url->path;
	} catch (\Exception $e) {
	}

	return $url;
}

// prevede URL cestu k index.html souboru, odebere i "/" ze zacatku
// /lorem/ipsum => lorem/ipsum/index.html
// /lorem/ipsum/index.html => lorem/ipsum/index.html
function fixOutputFilepath($url)
{
	$url = trim($url, '/');
	if (Nette\Utils\Strings::endsWith($url, '.html') || Nette\Utils\Strings::endsWith($url, '.htm')) {
		return $url;
	}
	if (empty($url)) {
		return 'index.html';
	}

	return trim($url.'/index.html', '/');
}

// jako nahrada pro get_permalink
function getUrl($id)
{
	$permalink = get_permalink($id);
	$url = clearHostUrl($permalink);

	return $url;
}

// pro generovani cesty na disku
function getOutputPath($id)
{
	$url = getUrl($id);

	return fixOutputFilepath($url);
}

// rozdeli pole $items do casto po $perPage, a pripravi URL, kde # nahradi za cislo stranky
function createPagination(array $items, $urlTemplate, $perPage = 10)
{
	$result = [];
	$itemsCount = count($items);
	$pagesCount = max(1, (int) ceil($itemsCount / $perPage)); // at least 1 page

	$urls = valKeys(range(1, $pagesCount));

	foreach ($urls as $key => $val) {
		$urls[$key] = str_replace('#', $val, $urlTemplate);
	}

	for ($page = 0; $page < $pages; ++$page) {
		$pageNum = $page + 1;
		$pageItems = array_slice($items, $page * $perPage, $perPage);
		$result[] = [
			'num' => $pageNum,
			'url' => $urls[$pageNum],
			'urls' => $urls,
			'pagesCount' => $pagesCount,
			'itemsCount' => $pagesCount,
			'isFirstPage' => 0 === $page,
			'isLastPage' => $pageNum === $pages,
			'items' => $pageItems,
		];
	}

	return $result;
}

// working with menus
function fetchMenuItems($location)
{
	$menuLocations = get_nav_menu_locations();
	$items = wp_get_nav_menu_items($menuLocations[$location]) ?: [];

	return $items;
}

function normalizeMenuItem($item)
{
	return [
		'title' => $item->title,
		'url' => clearBaseUrl($item->url),
	];
}

function getMenuItems($name)
{
	$items = fetchMenuItems($name);

	return map($items, 'normalizeMenuItem');
}

/*
// mame post_type `job`
loadSingles('job');
*/
// vrati pole namapovane jako id => data k post_typu
// data k post typu se loaduji automaticky z funkce `fetch_{post_type}($id)` - tu musis definovat
function loadSingles($post_type, $query_args = [])
{
	$ids = (new WP_Query($query_args + [
		'post_type' => $post_type,
		'nopaging' => true,
		'fields' => 'ids',
	]))->posts;

	$ids = valKeys($ids);

	return map($ids, 'fetch_'.$post_type);
}

// maybe change to return data for <picture> or something
function imageSizes($id, $fn = 'get_image_url')
{
	$sizes = get_intermediate_image_sizes();
	$result = filter(map(valKeys($sizes), function ($size) use ($id, $fn) {
		return call_user_func($fn, $id, $size);
	}));

	return empty($result) ? null : $result;
}

function thumbnailSizes($id)
{
	return imageSizes($id, 'get_thumbnail_url');
}

function generateTemplates($collections)
{
	$templates = [];

	foreach ($collections as $post_type => $entities) {
		foreach ($entities as $id => $page) {
			$urlPath = getOutputPath($id);

			$templateName = $page['template'] ?? 'single_'.$post_type;

			$templates[] = [
				'template' => "src/templates/$templateName.pug", // pocita s tim ze templaty ve wordpressu sedi s templaty v pugu
				'data' => [$urlPath => ['id' => $id]],
			];
		}
	}

	return $templates;
}

function metaFields($id, $metas)
{
	return map(valKeys($metas), function($name) use ($id) {
		return meta($id, $name);
	});
}

Ukazka pouziti (do functions.php)

// ukazkova funkce pro nacitani stranky, kde se resi ruzne meta fieldy pro ruzne sablony
function fetch_page($id)
{
	$template = basename(get_page_template_slug($id), '.php');

	// common for all pages
	$result = [
		'id' => $id,
		'template' => $template,
		'thumbnail' => thumbnailSizes($id),
		'title' => get_the_title($id),
	];

	$result['gallery'] = map(meta_array($id, 'gallery'), 'imageSizes');

	// loading template specific things
	switch ($template) {
		case 'front-page':
			$result['meta'] = metaFields($id, ['only_on_homepage', 't_text']);
			$result['main_content'] = applyContentFilter(meta($id, 'main_content'));

			break;
		case 'about':
			$result['somethingOnAboutPage'] = meta($id, 'only_on_about');
			$result['gallery'] = map(meta_array($id, 'gallery'), 'imageSizes');
			break;
	}

	return $result;
}

// ukazkova funkce pro nacitani post typu job
function fetch_case_study($id)
{
	return [
		'id' => $id,
		'fake' => true,
	];
}

function loadSettings()
{
	return [
		'site' => [
			'name' => get_bloginfo('name'),
		],
	];
}

// Default boilerplate
function createMangowebBuilderDataset()
{
	$pages = loadSingles('page');
	$case_studies = loadSingles('case_study');

	$data = [
		'manifest' => 'src/data/manifest.json', // from mango.yaml
		'settings' => loadSettings(), // globalni settings atd.
		'post_type' => [
			// tady jsou post typy... napamovany jako [ id => data ], takze v sablone, kdyz mas ID neceho tak das `post_type.case_study[id_case_study]` a mas data
			'page' => $pages,
			'case_study' => $case_studies,
		],
	];

	$templates = generateTemplates($data['post_type']);

	return [
			'data' => $data,
			'templates' => $templates,
	];
}

Bonus round: performance

Pokud to mas blbe udelany a opakuje se dokola spousteni stejnejch funkci, se stejnejma parametrama, ktery vracej stejny vysledky. Tak se tam da zapnout maly cachovani - jen v ramci jednoho behu skriptu...

class Cached {
	public static $enabled = TRUE;

	private static $cache = [];

	public static function __callStatic($name, $arguments) {
			$key = serialize([ $name, $arguments ]);
			if(isset(self::$cache[$key])) {
					return self::$cache[$key];
			}
			return self::$cache[$key] = call_user_func_array($name, $arguments);
	}
}

function narocnaFunkceCoNecoVraci($hodnota) {
	sleep(1); // simulace narocnosti - trva 1 sekundu
	return "vraceno:".$hodnota;
}

// tenhle foreach bude trvat cca 100 sekund
for($i = 0; $i < 100; $i++) {
	narocnaFunkceCoNecoVraci('foobar');
}

// tenhle foreach bude trvat cca 1 sekundu
for($i = 0; $i < 100; $i++) {
	// Staci pridat `Cached::` a stejne parametry budou vracet vzdy stejne vysledky
	Cached::narocnaFunkceCoNecoVraci('foobar');
}