Phalcon assign view variable inconsistent - phalcon

Having a problem getting consistent behavior assigning variables to the view. For example:
In controller:
$this->view->media = Media::findFirst(['groupId=0', 'order' => 'RAND()', 'limit' => 1]);
In view:
{% if media is defined %}
<div class="thumbnail">
<img src="{{ static_url('img/media/thumbs/' ~ media.name) }}" class="img-round">
<div class="caption">
<h3>{{ media.title }}</h3>
<p>{{ media.description }}</p>
</div>
</div>
{% endif %}
Which is parsed like:
<?php if (isset($media)) { ?>
<div class="thumbnail">
<img src="<?php echo $this->url->getStatic('img/media/thumbs/' . $this->media->name); ?>" class="img-round">
<div class="caption">
<h3><?php echo $this->media->title; ?></h3>
<p><?php echo $this->media->description; ?></p>
</div>
</div>
<?php } ?>
The problem is that when the parsed version of the template, $media is not accessible via $this so the isset($media) passes, but the references to $this->media returns nothing.
Any way to force $media to be local in scope?

I think I got it.
Misbehaviour description
You have probably declared a media module in your DI(). I was trying quite a lot to reproduce that error, and got it finally when i set a dumb media service among configuration files:
$di->set('media', function() {
return new \stdClass();
});
and than got the same behavior you have - Volt during compilation is not sure, what variable to use and choses $this->media (DI::get('media')) instead of $media or $this->view->media var for obtaining data.
Solution
If you dont want to resign from calling you findFirst result under that variable name, you may bypass that by using View in volt directly:
{% if view.media is defined %}
<div class="thumbnail">
<img src="{{ static_url('img/media/thumbs/' ~ view.media.name) }}" class="img-round">
<div class="caption">
<h3>{{ view.media.title }}</h3>
<p>{{ view.media.description }}</p>
</div>
</div>
{% endif %}
it will generate $this->view->media calls instead of $this->media ones.
+1 on that question.

Related

How can I build a query in my controller action that indexes parent models, with parent-specific children nested using foreach loops?

I'm currently trying to create an index of cards depicting rooms, with their associated desks indexed within them, using nested foreach loops within the view:
<div class="grid grid-cols-1 gap-3 ">
#foreach ($rooms as $room)
<div class="bg-white text-center h-auto">
{{ $room->name }}
<div class="m-3">
<div class="lg:grid lg:grid-cols-6 gap-3">
#foreach ($desks as $desk)
<div class="bg-red-200 text-center h-10">
Desk {{ $desk->id }}
</div>
#endforeach
</div>
</div>
</div>
#endforeach
</div>
The Room controller index action currently looks like this:
class RoomController extends Controller
{
public function index()
{
return view('index', [
'rooms' => Room::all(),
'desks' => Desk::all()
]);
}
}
I'm aware that Desk::all() is indexing all desks for all rooms. Instead, I need to build a query that indexes desks belonging only to each room. My model relationships are defined as:
Room hasMany desks
Desk belongsTo room
Any help would be much appreciated, thanks.
instant doing like rooms::all and desk::all use can simply run an eloquent query like:-
room::with("desks")->get();
note :-you need to make a relation in your room model with name "desks" like this :-
public function desks(){
return $this->hasMany(desk::class);
}
and your foreign key name must be like model name +_id
Managed to solve this. Desk::all() worked as I wanted to make all desks available at controller level. Instead I added a where clause to the nested foreach loop to filter by room_id:
<div class="grid grid-cols-1 gap-3 ">
#foreach ($rooms as $room)
<div class="bg-white text-center h-auto">
{{ $room->name }}
<div class="m-3">
<div class="lg:grid lg:grid-cols-6 gap-3">
#foreach ($desks->where('room_id', $room->id) as $desk)
<div class="bg-red-200 text-center h-10">
Desk {{ $desk->id }}
</div>
#endforeach
</div>
</div>
</div>

Dynamic computed property in html

I'm using php for creating set of computed properties in my app based on ID property of each object in my main array of data stored in property deals. So i have now computed properties like list_10_deals_cnt, list_20_deals_cnt, list_30_deals_cnt etc. Now, how can I create these dynamic created properties in span with class dials__cnt while looping my array of data? {{'list_'+el.id+'_deals_cnt'}} is not working as i wish, its display just a string like list_10_deals_cnt instead to display a computed value.
P.S. sorry about my english.
<div class="dials" id="app">
<div class="dials__column" v-for="(el, index) in deals">
<div class="dials__header">
<div>{{el.title}}</div>
<div>сделок: <span class="dials__cnt">{{`'list_'+el.id+'_deals_cnt'`}}</span>, <span></span> руб</div>
</div>
<draggable drag-class="draggable-ghost__class" class="dials__block" :list="el.items" group="deal" #change="log(el.id, $event)">
<div
class="dials__card"
v-for="(el, index) in el.items"
:key="el.title"
>
<div class="card__header">
<div class="card__title">{{ el.customer_name }}, {{ el.customer_company_name }}</div>
<div class="card__date">{{ el.created_at }}</div>
</div>
<div class="card__body">
<a :href="'/deal/detail/'+el.id" class="card__link">{{ el.title }}</a>
</div>
<div class="card__footer">
<span class="card__price">{{ el.price }} руб </span>
<span v-for="(tag, index) in el.tags" class="card__tag">{{ tag }}</span>
</div>
</div>
</draggable>
</div>
</div>
<script>
new Vue({
el: '#app',
data: {
deals: <?php echo json_encode($deals, JSON_UNESCAPED_UNICODE); ?>
},
computed: {
<?php
foreach ($deals as $k=>$v) {
echo 'list_'.$v->id.'_deals_cnt: function() {return this.deals['.$k.'].items.length},';
}
?>
},
methods: {
log: function(id, evt) {
if (evt.hasOwnProperty('added')) {
let dealID = evt.added.element.id;
console.log('сделка ID '+dealID+' перемещена в статус '+id+'. Отправляем аякс запрос на обновление статуса в бд');
// ajax for update
}
}
}
});
</script>
Hi
Problem 1
you will not be able to get the value of computed property by using this
{{`'list_'+el.id+'_deals_cnt'`}}
for the same reason as console.log('vari' + 'able') doesn't print out value of variable to the console.
( Vue evaluates whatever is in between {{ }} as an expression ).
Solution
I suppose, you can either use the deals property directly in html as shown below without using a computed property
<div class="dials__column" v-for="(el, index) in deals">
<div class="dials__header">
<div>{{el.title}}</div>
<div>сделок: <span class="dials__cnt">{{ el.items.length }}</span>, <span></span> руб</div>
</div>
----------- rest of the code
or you can create a computed property based on deals data property and use that to loop in html using v-for.
Problem 2
I don't think below code is valid php string. although it becomes irrelevant if you use first solution above.
<?php
foreach ($deals as $k=>$v) {
echo 'list_'.$v->id.'_deals_cnt: function() {return this.deals['.$k.'].items.length},';
}
?>
the ' inside ['.$k.'] should be escaped.
Solution
echo 'list_'.$v->id.'_deals_cnt: function() {return this.deals[\'.$k.\'].items.length},';

Flask: how to link wtforms with jinja2-templates using bootstrap

it seems a stupid question but I do not really get the connection.
I use tghe latest bootstrap version ad have this template:
<div class="form-group"{% if form.name.errors %} error{% endif %}>
<label for="name" class="col-md-2 control-label">Name:</label>
<div class="col-md-10">
<input type="text" class="form-control" id="name" placeholder="Enter your name here">
{% for error in form.name.errors %}
<span class="help-inline">[{{ error }}]</span><br>
{% endfor %}
</div>
</div>
The field is rendered as desired but no matter what I enter there my form will raise the error, that this field is required.
Please let me know what I miss here - how do I link the field in the template to my form properly?
The issue was that the "name" attribute was missing - only having the "id" is insufficient.

Wrap Phalcon flash messages in HTML element

Is there a possibility to wrap flash messages in an element? I want to have no html element at all when there are no messages and have an extra div containing all messages if there is any message.
It would be enough if I could at least get information whether there are any flash messages and then code it myself, but it seems to me that neither Phalcon\Flash\Direct nor Phalcon\Flash\Session allow you to access current message count or wrap messages in your own html element.
Just configure your flash service to just output the message:
$this->flash->setAutomaticHtml(false);
Also, when outputting a message, it's automatically echoed.
If you want to just return a string without echoing it to the output buffer use:
$this->flash->setImplicitFlush(false);
These methods aren't in the main documentation page, but you should always look at the class reference too, you might find very usefull information there :)
EDIT
To return only messages you use setAutomaticHtml to false, setImplicitFlush has nothing to do with it. Also to know if a message exists use something like this:
$this->flashSession->has('error');
I have ended with following code. I basically had to generate output myself.
<?php
$messages = $this->flashSession->getMessages();
if ( count($messages) > 0) {
?>
<div class="basic-bg">
<div class="main-column">
<div class="flash-messages">
<?php
foreach ($messages as $messageType => $messageArray) {
foreach ($messageArray as $message) {
echo "<div class=\"flash-$messageType\">$message</div>";
}
}
?>
</div>
</div>
</div>
<?php } ?>
I know this is an old thread, but how about implementing an extended class to make sure your message string is still correctly escaped?
This is the class I've used to implement Bootstrap 3 dismissable messages:
<?php
namespace Ext;
/**
* Extension to Phalcon Framework to implement Bootstrap 3 dismissable messages.
* Pass mappings of phalcon to bootstrap classes to construct
* #link https://docs.phalconphp.com/uk/latest/reference/flash.html Phalcon flash docs
* #author Kevin Andrews <kevin#zvps.uk>
*/
class FlashBootstrap extends \Phalcon\Flash\Session
{
/**
* Correctly escapes the message while building a Bootstrap 3
* compatible dismissable message with surrounding html.
* #param string $type
* #param string $message
* #return void
*/
public function message($type, $message)
{
$bootstrapCssClass = $this->_cssClasses[$type];
$errorType = ucfirst($type);
$bootstrapMessage = "<div class=\"alert alert-{$bootstrapCssClass} alert-dismissible\" role=\"alert\"><button type=\"button\" class=\"close\" data-dismiss=\"alert\" aria-label=\"Close\"><span aria-hidden=\"true\">×</span></button><strong>{$errorType}:</strong> {$this->getEscaperService()->escapeHtml($message)}</div>";
parent::message($type, $bootstrapMessage);
}
}
Also for completeness the initialisation for DI:
<?php
$di->set('flash', function() {
$bootstrapFlash = new Ext\FlashBootstrap(array(
'error' => 'alert alert-danger alert-dismissible',
'success' => 'alert alert-success alert-dismissible',
'notice' => 'alert alert-info alert-dismissible',
'warning' => 'alert alert-dismissible',
));
$bootstrapFlash->setAutoescape(false);
$bootstrapFlash->setAutomaticHtml(false);
return $bootstrapFlash;
});
This also has the advantage that the ->success() ->error() ->notice() and ->warning() helper methods will all go through this code and produce nicely formatted messages wrapped in the desired HTML.
{% if flash.has('notice')==true OR flash.has('success') %}
{% for notif in flash.getMessages('success') %}
<div class="notif_global success">
<div class="notif_global-title">Успешно</div>
<div class="notif_global-content">{{ notif }}</div>
<div class="notif_global-close ico_close"></div>
</div>
{% endfor %}
{% for notif in flash.getMessages('notice') %}
<div class="notif_global success">
<div class="notif_global-title">Сообщение</div>
<div class="notif_global-content">{{ notif }}</div>
<div class="notif_global-close ico_close"></div>
</div>
{% endfor %}
{% endif %}
{% if flash.has('warning')==true OR flash.has('error') %}
{% for notif in flash.getMessages('warning') %}
<div class="notif_global error">
<div class="notif_global-title">Предупреждение</div>
<div class="notif_global-content">{{ notif }}</div>
<div class="notif_global-close ico_close"></div>
</div>
{% endfor %}
{% for notif in flash.getMessages('error') %}
<div class="notif_global error">
<div class="notif_global-title">Ошибка</div>
<div class="notif_global-content">{{ notif }}</div>
<div class="notif_global-close ico_close"></div>
</div>
{% endfor %}
{% endif %}

How do i dynamically/evaluate via expression json property in django template

reading in a JSON for sport. using a partial for matchup markup.
awayteam and home team for most part share identical markup but the JSON properties which I have no control are such below:
<div class="away {{game.awayTeam_last_name|slugify}}">
<a href="#" title="{{game.awayTeam_first_name}} {{game.awayTeam_last_name}}">
<span class="{{league}}-logo"></span>
<span class="city">{{game.awayTeam_first_name}}</span>
{% if game.event_status != "pre-event" %}
<span title="score">{{game.awayTeam_score}}</span>
{% else %}
<span title="record entering game">(0-0)</span>
{% endif %}
</a>
</div>
<span>#</span>
<div class="home {{game.homeTeam_last_name|slugify}}">
<a href="#" title="{{game.homeTeam_first_name}} {{game.homeTeam_last_name}}">
<span class="{{league}}-logo"></span>
<span class="city">{{game.homeTeam_first_name}}</span>
{% if game.event_status != "pre-event" %}
<span title="score">{{game.homeTeam_score}}</span>
{% else %}
<span title="record entering game">(0-0)</span>
{% endif %}
</a>
</div>
is there a way to shrink/refactor the above like some expression valuator to make home and away passed via a variable.
didn't answer own question but i did get Data guys to better format the data, so became..
awayteam: { first_name:'', last_name:'', score:'' }
hometeam: { first_name:'', last_name:'', score:'' }
allowing me to shrink in half the template =)