<?php
// https://forum.nette.org/cs/34970-rozne-ceny-za-roznych-okolnosti#p218919
class Product
{
private Price $price;
public function getPrice(): Price
{
return $this->price;
}
}
//
public function getPrice(...$modifiers): Price
{
$price = $this->price;
foreach($modifiers as $modifier) {
$price = $price->modify($modifier);
}
return $price;
}
//
class PriceResolver
{
private array $modifiers = [];
public function setCustomer(IModifier $modifier): void
{
$this->modifiers[] = $modifier;
}
public function setSeason(IModifier $modifier): void
{
$this->modifiers[] = $modifier;
}
public function setOtherRule(IModifier $modifier): void
{
$this->modifiers[] = $modifier;
}
public function resolve(Product $product): Price
{
$price = $product->getPrice();
foreach($modifiers as $modifier) {
$price = $price->modify($modifier);
}
return $price;
}
}
class Price
{
private float $amount;
private string $currency;
public function __construct(float $amount, string $currency)
{
$this->amount = $amount;
$this->currency = $currency;
}
public function getAmount(int $precision = 2): float
{
return round($this->amount, $precision);
}
public function getCurrency(): string
{
return $this->currency;
}
public function modify(Modifier $modifier): Price
{
if($modifier->isPercentage()) {
$amount = $this->getAmount(4) * (100 + $modifier->getAmount()) / 100;
}
else {
$amount = $this->getAmount(4) + $modifier->getAmount();
}
return new self($amount, $this->currency);
}
public function multiply(float $multiplier): Price
{
return new self($this->amount * $multiplier, $this->currency);
}
public function divide(float $divisor): Price
{
return new self($this->amount / $divisor, $this->currency);
}
public function add(Price $price): Price
{
if($this->currency !== $price->getCurrency()) {
throw new CurrencyMismatchException("Currency missmatch");
}
return new self($this->amount + $price->getAmount(4), $this->currency);
}
public function minus(Price $price): Price
{
if($this->currency !== $price->getCurrency()) {
throw new CurrencyMismatchException("Currency missmatch");
}
return new self($this->amount - $price->getAmount(4), $this->currency);
}
public function equals(Price $other): bool
{
return $this->getAmount(4) === $other->getAmount(4) and $this->currency === $other->getCurrency();
}
}
class Modifier
{
const TYPE_PERCENTAGE = "P";
const TYPE_ABSOLUTE = "A";
private string $type;
private float $amount;
public function __construct(string $type, float $amount)
{
$this->type = $type;
$this->amount = $amount;
}
public function getType(): string
{
return $this->type;
}
public function getAmount(int $precision = 2): float
{
return round($this->amount, $precision);
}
public function cumulate(Modifier $modifier): Modifier
{
if($this->type !== $modifier->getType()) {
throw new TypeMissmatchException("Type missmatch");
}
return new self($this->type, $this->amount + $modifier->getAmount(4));
}
public function isPercentage(): bool
{
return $this->type === self::TYPE_PERCENTAGE;
}
public function isAbsolute(): bool
{
return $this->type === self::TYPE_ABSOLUTE;
}
}
<?php
namespace App\FrontModule\Filters;
use Nette;
use Andweb;
class SotioFilter
{
/**
* Andweb\Localization\ITranslator
*/
private $translator;
public function __construct(Andweb\Localization\ITranslator $translator)
{
$this->translator = $translator;
}
public function register($template)
{
$template->addFilter(null, [$this, 'loader']);
}
public function loader($filter)
{
if (method_exists(__CLASS__, $filter))
{
$args = func_get_args();
array_shift($args);
return call_user_func_array(array(__CLASS__, $filter), $args);
}
return NULL;
}
public function readMore($s)
{
$s = preg_replace('~(<div>--START--</div>)(.+)<div>--END--</div>~Usi',
'<div class="read-more-wrapper">$2</div><div class="read-more-btn-wrapper"><a href="">'.$this->translator->translate('READ MORE').'</a></div>',
$s
);
return $s;
}
}
<template>
<div>
<p>
<custom-checkbox>Simple example</custom-checkbox>
</p>
<p>
<custom-checkbox :disabled="true">Disabled</custom-checkbox>
</p>
</div>
</template>
<script>
import CustomCheckbox from './CustomCheckbox'
export default {
components: { CustomCheckbox }
}
</script>
<style scoped>
</style>
<template>
<div>
<tree-list
:children="treeItems"
:parents="[]"
/>
</div>
</template>
<script>
import TreeList from './TreeList.vue'
export default {
components: {TreeList},
compontents: {
TreeList
},
data () {
return {
treeItems: [
{
title: 'First top level',
children: [
{
title: 'First sub level',
children: [
{title: 'First sub-sub level'},
{title: 'Second sub-sub level'},
{title: 'Third sub-sub level'}
]
},
{
title: 'Second sub level',
children: [
{title: 'First sub-sub level'},
{title: 'Second sub-sub level'},
{title: 'Third sub-sub level'}
]
},
{
title: 'Third sub level',
children: [
{title: 'First sub-sub level'},
{title: 'Second sub-sub level'},
{title: 'Third sub-sub level'}
]
}
]
},
{
title: 'Second top level',
children: [
{
title: 'First sub level',
children: [
{title: 'First sub-sub level'},
{title: 'Second sub-sub level'},
{title: 'Third sub-sub level'}
]
},
{
title: 'Second sub level',
children: [
{title: 'First sub-sub level'},
{title: 'Second sub-sub level'},
{title: 'Third sub-sub level'}
]
},
{
title: 'Third sub level',
children: [
{title: 'First sub-sub level'},
{title: 'Second sub-sub level'},
{title: 'Third sub-sub level'}
]
}
]
},
{
title: 'Third top level',
children: [
{
title: 'First sub level',
children: [
{title: 'First sub-sub level'},
{title: 'Second sub-sub level'},
{title: 'Third sub-sub level'}
]
},
{
title: 'Second sub level',
children: [
{title: 'First sub-sub level'},
{title: 'Second sub-sub level'},
{title: 'Third sub-sub level'}
]
},
{
title: 'Third sub level',
children: [
{title: 'First sub-sub level'},
{title: 'Second sub-sub level'},
{title: 'Third sub-sub level'}
]
}
]
}
]
}
}
}
</script>
<style scoped>
</style>
/*
https://stackblitz.com/edit/superlasice-efzixd?file=src%2FApp.vue
*/
<template>
<div id="app">
<button v-on:click="add">add new row</button>
<p>Total price {{ total }} | {{ total2 }}</p>
<ul>
<li v-for="(item, index) in items">
Name<br />
<input type="text" v-model="item.name" />
<br />
Quantity<br />
<input type="number" v-model.number="item.quantity" min="1" />
<br />
Price<br />
<input
type="number"
v-model.number="item.price"
min="0.00"
max="10000"
step="1"
/>
<br />
Total (readonly) <br />
<input v-model="totalItems[index]" readonly /> <br />
total in row:
{{ totalItem(item) }}
<br />
<br />
<button v-on:click="remove(index)">Delete row</button>
<hr />
</li>
</ul>
</div>
</template>
<script>
export default {
name: 'App',
data() {
return {
items: [
{ name: 'A', quantity: 1, price: 20 },
{ name: 'B', quantity: 2, price: 30 },
],
};
},
methods: {
add() {
this.items.push({
name: 'New product',
quantity: 0,
price: 0,
});
},
remove(index) {
this.items.splice(index, 1);
},
totalItem(item) {
return item.price * item.quantity;
},
},
computed: {
totalItems() {
let arr = [];
this.items.forEach((item) => {
arr.push(parseFloat(item.price) * parseFloat(item.quantity));
});
return arr;
},
total() {
let sum = 0;
this.items.forEach((item) => {
sum += parseFloat(item.price) * parseFloat(item.quantity);
});
return sum;
},
// another approach how to sum
total2() {
return this.items.reduce((prev, item) => {
return prev + item.price * item.quantity;
}, 0);
},
},
};
</script>
<?php
try
{
// ...
$this->redirect('PresenterWhatEver:default');
}
catch (\Exception $e)
{
// posle se to dal kdyz premserujeme - redirect vyhazuje abortException ...
if ($e instanceof Nette\Application\AbortException)
{
throw $e;
}
}
/* https://github.com/hekigan/vue-directive-tooltip */
/*
* @author: laurent blanes <laurent.blanes@gmail.com>
* @tutorial: https://hekigan.github.io/vue-directive-tooltip/
*/
import Tooltip from './tooltip.js';
const BASE_CLASS = 'vue-tooltip';
const POSITIONS = ['auto', 'top', 'bottom', 'left', 'right'];
const SUB_POSITIONS = ['start', 'end'];
/**
* usage:
*
* // basic usage:
* <div v-tooltip="'my content'">
* or
* <div v-tooltip="{content: 'my content'}">
*
* // change position of tooltip
* // options: auto (default) | bottom | top | left | right
*
* // change sub-position of tooltip
* // options: start | end
*
* <div v-tooltip.top="{content: 'my content'}">
*
* // add custom class
* <div v-tooltip="{class: 'custom-class', content: 'my content'}">
*
* // toggle visibility
* <div v-tooltip="{visible: false, content: 'my content'}">
*/
export default {
name: 'tooltip',
config: {},
install (Vue, installOptions) {
Vue.directive('tooltip', {
bind (el, binding, vnode) {
if (installOptions) {
Tooltip.defaults(installOptions);
}
},
inserted (el, binding, vnode, oldVnode) {
if (installOptions) {
Tooltip.defaults(installOptions);
}
let options = filterBindings(binding, vnode);
el.tooltip = new Tooltip(el, options);
if (binding.modifiers.notrigger && binding.value.visible === true) {
el.tooltip.show();
}
if (binding.value && binding.value.visible === false) {
el.tooltip.disabled = true;
}
},
componentUpdated (el, binding, vnode, oldVnode) {
if (hasUpdated(binding.value, binding.oldValue)) {
update(el, binding, vnode, oldVnode);
}
},
unbind (el, binding, vnode, oldVnode) {
el.tooltip.destroy();
}
});
}
};
/**
*
* @param {*} vnode component's properties
* @param {*} oldvnode component's previous properties
* @return boolean
*/
function hasUpdated (value, oldValue) {
let updated = false;
if (typeof value === 'string' && value !== oldValue) {
updated = true;
} else if (isObject(value)) {
Object.keys(value).forEach(prop => {
if (value[prop] !== oldValue[prop]) {
updated = true;
}
});
}
return updated;
}
/**
* Sanitize data
* @param {*} binding
* @param {*} vnode
* @return {*} filtered data object
*/
function filterBindings (binding, vnode) {
const delay = !binding.value || isNaN(binding.value.delay) ? Tooltip._defaults.delay : binding.value.delay;
if (binding.value.ref) {
if (vnode.context.$refs[binding.value.ref]) {
binding.value.html = vnode.context.$refs[binding.value.ref];
} else {
console.error(`[Tooltip] no REF element [${binding.value.ref}]`); // eslint-disable-line
}
}
return {
class: getClass(binding),
id: (binding.value) ? binding.value.id : null,
html: (binding.value) ? binding.value.html : null,
placement: getPlacement(binding),
title: getContent(binding),
triggers: getTriggers(binding),
fixIosSafari: binding.modifiers.ios || false,
offset: (binding.value && binding.value.offset) ? binding.value.offset : Tooltip._defaults.offset,
delay
};
}
/**
* Get placement from modifiers
* @param {*} binding
*/
function getPlacement ({modifiers, value}) {
let MODS = Object.keys(modifiers);
if (MODS.length === 0 && isObject(value) && typeof value.placement === 'string') {
MODS = value.placement.split('.');
}
let head = 'bottom';
let tail = null;
for (let i = 0; i < MODS.length; i++) {
const pos = MODS[i];
if (POSITIONS.indexOf(pos) > -1) {
head = pos;
}
if (SUB_POSITIONS.indexOf(pos) > -1) {
tail = pos;
}
}
// console.log((head && tail) ? `${head}-${tail}` : head);
// return 'auto';
return (head && tail) ? `${head}-${tail}` : head;
}
/**
* Get trigger value from modifiers
* @param {*} binding
* @return String
*/
function getTriggers ({modifiers}) {
let trigger = [];
if (modifiers.notrigger) {
return trigger;
} else if (modifiers.manual) {
trigger.push('manual');
} else {
if (modifiers.click) {
trigger.push('click');
}
if (modifiers.hover) {
trigger.push('hover');
}
if (modifiers.focus) {
trigger.push('focus');
}
if (trigger.length === 0) {
trigger.push('hover', 'focus');
}
}
return trigger;
}
/**
* Check if the variable is an object
* @param {*} value
* @return Boolean
*/
function isObject (value) {
return typeof value === 'object';
}
/**
* Check if the variable is an html element
* @param {*} value
* @return Boolean
*/
function isElement (value) {
return value instanceof window.Element;
}
/**
* Get the css class
* @param {*} binding
* @return HTMLElement | String
*/
function getClass ({value}) {
if (value === null) {
return BASE_CLASS;
} else if (isObject(value) && typeof value.class === 'string') {
return `${BASE_CLASS} ${value.class}`;
} else if (Tooltip._defaults.class) {
return `${BASE_CLASS} ${Tooltip._defaults.class}`;
} else {
return BASE_CLASS;
}
}
/**
* Get the content
* @param {*} binding
* @return HTMLElement | String
*/
function getContent ({value}, vnode) {
if (value !== null && isObject(value)) {
if (value.content !== undefined) {
return `${value.content}`;
} else if (value.id && document.getElementById(value.id)) {
return document.getElementById(value.id);
} else if (value.html && document.getElementById(value.html)) {
return document.getElementById(value.html);
} else if (isElement(value.html)) {
return value.html;
} else if (value.ref && vnode) {
return vnode.context.$refs[value.ref] || '';
} else {
return '';
}
} else {
return `${value}`;
}
}
/**
* Action on element update
* @param {*} el Vue element
* @param {*} binding
*/
function update (el, binding, vnode, oldVnode) {
if (typeof binding.value === 'string') {
el.tooltip.content(binding.value);
} else {
if (binding.value && binding.value.class && binding.value.class.trim() !== el.tooltip.options.class.replace(BASE_CLASS, '').trim()) {
el.tooltip.class = `${BASE_CLASS} ${binding.value.class.trim()}`;
}
el.tooltip.content(getContent(binding, vnode));
if (!binding.modifiers.notrigger && binding.value && typeof binding.value.visible === 'boolean') {
el.tooltip.disabled = !binding.value.visible;
return;
} else if (binding.modifiers.notrigger) {
el.tooltip.disabled = false;
}
const dir = vnode.data.directives[0];
if (dir.oldValue.visible !== dir.value.visible) {
if (!el.tooltip.disabled) {
el.tooltip.toggle(dir.value.visible);
}
}
}
}
create table tbl_values (
id int unsigned not null primary key,
`Name` varchar(10),
`Group` varchar(10),
`Value` int
);
insert into tbl_values values
(1, 'Pete', 'A', 10),
(2, 'Pete', 'B', 20),
(3, 'John', 'A', 10);
-- 1. Create an expression that builds the columns
set @sql = (
select group_concat(distinct
concat(
"sum(case when `Group`='", `Group`, "' then `Value` end) as `", `Group`, "`"
)
)
from tbl_values
);
-- 2. Complete the SQL instruction
set @sql = concat("select Name, ", @sql, " from tbl_values group by `Name`");
-- 3. Create a prepared statement
prepare stmt from @sql;
-- 4. Execute the prepared statement
execute stmt;
<?php
// https://git.andweb.cz/brandcloud/app/-/blob/bcnew/app/BrandCloudModule/components/SharingForm.php#L148
$form->addSubmit('share', 'Share')
->onInvalidClick[] = function () use ($form) {
// we have to set recipients as items to revalidate recipients field
// TODO: posibly change multiselectbox to some other implementation which works only with raw value
if ($form['sharing_type']->getValue() === 'email') {
$form['recipients']->setItems((array) $form['recipients']->getRawValue(), false);
$form->validate();
}
};
$form->onValidate[] = [$this, 'validateForm'];
$form->onSubmit[] = [$this, 'submitForm'];
$form->onSuccess[] = [$this, 'successForm'];
$form->setDefaults($this->getFormDefaults());
return $form;