For a project I'm using pommbundle, it's perfect for generating entities with an existing database.
In my controller:
$catalogues = $this->get('pomm')['my_db1']
->getModel('\AppBundle\Entity\MyDb1\PublicSchema\CatalogueModel')
->findAll();
return this->render(
'SiteBundle:Default:homePage.html.twig',
array('catalogues'=>$catalogues));
But how can I access the variable inside my view (Twig)
{% for catalogue in catalogues %}
{{dump(catalogue)}} --> value inside
{% endfor %}
Result dump
Catalogue {#1132 ▼
#container: array:13 [▼
"ID" => 8
"Code" => "MATIÈRE PREMIÈRE"
"Actif" => true
"DateAjout" => DateTime {#1212 ▶}
"Index" => 0
"PriseCommande" => false
"Description" => ""
"Couleur" => "Green"
"CouleurText" => "#000000"
"Tarif" => null
"WebActif" => false
"WebTitre" => null
"WebDescription" => null ]
-status: 1 }
catalogue.ID (not working) catalogue.container.ID (not working)
with catalogue.get('ID') works but it's the best way?
Other question
If my entity has a relation, e.g. WebActif -> relation with another table,
How to access Webactif because the dump returns only an ID.Do I have to create my own method?
Is it possible to show a basic example?
The Model::findAll method returns an iterator on database results. When this iterator is traversed, it returns entities filled with converted values.
Note: you’d better not use upper case letters in your column names as it will lead to confusion and it will not work properly with Pomm flexible entities. (same applies for table names).
<dl>
{% if catalogues.isEmpty() %}
<dt>No results found.</dt>
{% else %}
<dt>There are {{ catalogues.count() }} results:</dt>
{% for catalogue in catalogues %}
<dd>{{ catalogue.code }} (added the {{ catalogue.date_ajout.format('d-m-Y') }}){% if catalogue.actif %} OK {% endif %}</dd>
{% endfor %}
{% endif %}
</dl>
Edit: Since your comment says your database contains capitalized column names here is an additional explanation on how flexible entities work.
(Official documentation about flexible entities is here)
When a flexible entity is hydrated by the iterator values, they are converted and then pushed with their name in the entity. This is why you can use the generic accessor $entity->get('MyColumn') because keys are preserved.
But flexible entities are strange beasts because they can change depending on the SELECT that decides the data sent to them. When such entity is created the getters and setters are virtually created using PHP’s __get and __set and __call functions.
This can seem weird but look at this example:
<?php
$entity = new MyEntity(['first_name' = 'John', 'last_name' => 'Doe']);
$entity['first_name']; // calls $entity->getFirstName() which defaults to $this->get('first_name');
It is then possible to override default accessors:
<?php
class MyEntity extends FlexibleEntity
{
/*
* Triggered by $entity['first_name']
* or $entity->first_name
*/
public function getFirstName(): string
{
return uc_words($this->get('first_name'));
}
public function getLastName(): string
{
return strtoupper($this->get('last_name'));
}
public function getName(): string
{
return sprintf("%s %s", $this->getFirstName(), $this->getLastName());
}
}
Then, in twig it is possible to simply do {{ entity.name }} to trigger the getName function.
As you can see, the column names are camel cased to create the virtual accessors, this operation can be reversed only if the original column names are in lower case.
Related
I am able to annotate description of a table by using asset URL (Location of the table in ADC).
Http request I am using is:
{table_location_url}/descriptions?api-version=2016-03-30
Json object is:
private static string DescriptionJson(string description)
{
return string.Format(#"
{{
""properties"" : {{
""key"": ""{0}"",
""fromSourceSystem"": false,
""description"": ""{1}""
}}
}}
", Guid.NewGuid().ToString("N"), description);
}
Annotating description for table is successful.
Now I am trying to annotate tag for a column using,
Http request: {table_location_url}/columnTags?api-version=2016-03-30
static string SampleAnnotationJson(string name)
{
return string.Format(#"
{{
""properties"" : {{
""key"": ""{0}"",
""fromSourceSystem"": false,
""columnName"": ""on_hold_text_key"",
""termId"": ""https://1194df16-3ae0-49aa-b48b-5c4da6e13689-imss-data-catalog.api.datacatalog.azure.com/catalogs/IMSS-Data-Catalog/glossaries/IMSS-Data-Catalog/terms/4b8fe89d-c92a-4aee-abe1-691a2cd52458"",
}}
", Guid.NewGuid().ToString("N"));
}
I am getting bad request erro r(400).
Am I missing anything?
The issue is with the json request, flower brackets are not closed properly. It works fine with the correction.
I have created a custom function I can access from the volt. The function seems to work fine, but I cannot manage to send the variable to the function. It sends the variable as text instead of its value.
The twig function:
$volt->getCompiler()->addFunction('getusergroup', function ($user) {
return \Models\User::getUserGroup($user);
});
The function in the Model:
public static function getUserGroup($user) {
return UserGroup::find(array('conditions' => 'user_id = ' . $user));
}
The lines in Twig to call the function:
{% for member in getusergroup(staff.id) %}
{{ member.Group.name }}
{% endfor %}
The error I get:
'Scanning error before 'staff->id' when parsing: SELECT
[Models\UserGroup].* FROM [Models\UserGroup] WHERE user_id =
$staff->id (78)' (length=131)
As you can see, in stead of $staff->id being an integer, it's the text.
How do I go about sending the actual ID to the function?
By the way, I am using twig in combination with Phalcon and followed the instructions in this article: http://phalcontip.com/discussion/60/extending-volt-functions
If you dump $user inside of your method public static function getUserGroup($user), you will receive this $staff->id, but you actually want something like 42.
To avoid this register the Volt function like this:
$volt->getCompiler()->addFunction('getusergroup', function ($resolvedArgs, $exprArgs) {
return 'Models\User::getUserGroup(' . $resolvedArgs . ')';
});
More info on Extending Volt Functions
As http://docs.phalconphp.com/en/latest/reference/models.html#understanding-records-to-objects says, you can edit the objects once its loaded in the memory.
$settingCategories = SettingCategory::find();
foreach($settingCategories as $settingCategory){
if($settingCategory->type == "2"){
$settingCategory->type = "asd";
$settingCategory->intersection = "asd";
}else{
$settingCategory->type = "blaa";
$settingCategory->intersection = "blaa";
}
$settingCategory->type = "test";
}
$this->view->setVar("settingCategories",$settingCategories);
type is still its default value when I loop through it with volt:
{% for settingCategory in settingCategories %}
<div class="tab-content">
<h4>{{ settingCategory.name }}</h4>
<h4>{{ settingCategory.type }}</h4> --> still (int) integer!?
<h4>{{ settingCategory.intersection }}</h4> --> undefined!?
</div>
{% endfor %}
When you are modifying a variable inside a foreach, you are modifying a "temporary variable". What it means is that since it is only a copy of the real variable, when you change it, the real value inside the array isn't changed. Now, on to what you could do to solve this:
Setters/Getters
I personally prefer this one. If what you want to do is data transformation (I.E. you change the value of a field from one thing to another, and you want to use the new value in your code everywhere), I would use setters and getters. Here is an example:
// This is inside your model
protected $type;
public function getType()
{
if ($this->type === 2) {
return "asd";
} else {
return $this->type;
}
}
public function setType($type)
{
if ($type === 2) {
$this->type = "asd";
} else {
$this->type = 1; // or $type, or anything really :)
}
}
Of course, in your code, you'll have to change $category->type to $category->getType() and $category->setType($type), based on whether you are reading the value or assigning something to it.
The Quick and Dirty Way
Well, if your use case is different, you can use your current code block with a simple modification. Change your foreach to foreach($settingCategories as &$settingCategory). The ampersand makes the variable be passed into the block as a reference (I.E. it is not a copy like your current case). That means changing it will change the real value.
I have an object like this:
$scope.phones = new Object();
$scope.phones['id1'] = {
"name":"Phone Name1",
"dateReleased":"2012-1-09 15:48:24"
};
$scope.phones['id2'] = {
"name": "Phone Name2",
"dateReleased":"2012-3-12 15:32:11"
};
$scope.phones['id3'] = {
"name": "Phone Name3",
"dateReleased":"2012-2-10 13:53:32"
};
I'm displaying this using ngRepeat. I'm not able to order by dateReleased. Also, ordering in reverse isn't working. My ngRepeat looks this:
<li ng-repeat="phone in phones | orderBy:dateReleased:true">
<p>{{phone.name}}</p>
<p>{{phone.dateReleased}}</p>
</li>
While ngRepeat can iterate a hash object, like $scope.phones in your example, the built-in orderBy filter will not work. I believe this is due to the way objects are stored. As other's have noted, you need to convert the hash to an array. While you can do this using the methods suggested above, I prefer to do it using a custom filter. This gives me the benefit of not having to alter my hash directly, and also let's me reuse the filter with other hashes.
yourApp.filter('orderObjectBy', function() {
return function(items, field, reverse) {
var filtered = [];
angular.forEach(items, function(item) {
filtered.push(item);
});
filtered.sort(function (a, b) {
return (a[field] > b[field] ? 1 : -1);
});
if(reverse) filtered.reverse();
return filtered;
};
});
This filter converts the object into a standard array and sorts it by the field you specify. You can use the orderObjectBy filter exactly like orderBy, including a boolean value after the field name to specify whether the order should be reversed. In other words, false is ascending, true is descending.
<li ng-repeat="phone in phones | orderObjectBy:'dateReleased':true">
<p>{{phone.name}}</p>
<p>{{phone.dateReleased}}</p>
</li>
I've got a post on my blog regarding this topic.
If you check the documentation it says that the expression in orderBy can be a function, a string or an Array. An therefore you need dateReleased to be a string: 'dateReleased'
Also you need your phones Object be an actual Array.
Try:
$scope.phones = [{
"name":"Phone Name1",
"dateReleased":"2012-1-09 15:48:24"
},{
"name": "Phone Name2",
"dateReleased":"2012-3-12 15:32:11"
},{
"name": "Phone Name3",
"dateReleased":"2012-2-10 13:53:32"
}];
<li ng-repeat="phone in phones | orderBy:'dateReleased':true">
<p>{{phone.name}}</p>
<p>{{phone.dateReleased}}</p>
</li>
Both of the other answers get you part way there, but not all the way...
You'll need to create a function on your scope that converts the object to an array like so:
$scope.phonesArray = function() {
var result = [];
angular.forEach($scope.phones, function(phone, id) {
result.push(phone);
});
return result;
};
Then you'd call that instead of your object in your ngRepeat:
<li ng-repeat="phone in phonesArray() | orderBy:'dateReleased':true">
<p>{{phone.name}}</p>
<p>{{phone.dateReleased}}</p>
</li>
Also: Notice that 'dateReleased' is a string, so it knows to $eval that string off of the current item, otherwise it will check the parent $scope.dateReleased, which doesn't exist.
Here is a plunk for what I think you're trying to do
EDIT: You can also "convert" the object to an array and store it on the $scope, if you're worried about the function to do so being too "expensive", but that shouldn't be an issue, as you're developing a clientside app for one user, and not a server application for many users, meaning you have a little wiggle room for "expensive". (Which it won't be anyway)
First of all, you need to understand that ng:filter and ng:orderBy work with Arrays (an ordered collection of items), but you're trying to use them on Object (_un_ordered collection of items). One possible approach is to collect all the objects into an array, then proceed with ng-repeat on it instead. Like this:
<ul ng-init="phones = [
{name:'Phone Name 1', dateReleased:'2011-1-09 15:48:24'}
, {name:'Phone Name 2', dateReleased:'2012-3-12 15:32:11'}
, {name:'Phone Name 3', dateReleased:'2012-2-10 13:53:32'}];
pred = '-dateReleased';" >
<li ng-repeat="phone in phones | orderBy:pred">
<p>{{phone.name}}</p>
<p>{{phone.dateReleased}}</p>
</li>
</ul>
I have a twig variable html. To show it in a twig template I do {{html}}.
That variable looks like:
<div>{{region_top}}</div><div>{{region_center}}</div>
region_* is a variable too. When Twig parses my html variable, it doesn't parse the inner variables (regions).
What I should do?
I have twig variable html. To show it in twig template I do {{html}}. That variable look like {{region_top}}{{region_center}}. region_* is variables too. When twig parse my html variable he didn't parse inner variables (regions). What can I should do?
Twig takes your strings as a literal string, meaning you'll see the content of the variable, escaped. If you want it to be able to display {{region_top}} as well, I'd recommend something like this:
{{html|replace({'{{region_top}}': region_top, '{{region_center}}': region_center})}}
If the content of your html variable is also dynamic (meaning it can contain more than just those two variables), I'd write a twig plugin which can do what you want. Writing plugins is pretty easy to do.
EDIT: Here's the extension I just finished writing.
EDIT 2: The extension now uses the environment to render the string, so it evaluates the string, instead of just replacing variables. This means your variable can contain anything a template can, and it will be render and escaped by Twig itself. I'm awesome.
<?php
/**
* A twig extension that will add an "evaluate" filter, for dynamic evaluation.
*/
class EvaluateExtension extends \Twig_Extension {
/**
* Attaches the innervars filter to the Twig Environment.
*
* #return array
*/
public function getFilters( ) {
return array(
'evaluate' => new \Twig_Filter_Method( $this, 'evaluate', array(
'needs_environment' => true,
'needs_context' => true,
'is_safe' => array(
'evaluate' => true
)
))
);
}
/**
* This function will evaluate $string through the $environment, and return its results.
*
* #param array $context
* #param string $string
*/
public function evaluate( \Twig_Environment $environment, $context, $string ) {
$loader = $environment->getLoader( );
$parsed = $this->parseString( $environment, $context, $string );
$environment->setLoader( $loader );
return $parsed;
}
/**
* Sets the parser for the environment to Twig_Loader_String, and parsed the string $string.
*
* #param \Twig_Environment $environment
* #param array $context
* #param string $string
* #return string
*/
protected function parseString( \Twig_Environment $environment, $context, $string ) {
$environment->setLoader( new \Twig_Loader_String( ) );
return $environment->render( $string, $context );
}
/**
* Returns the name of this extension.
*
* #return string
*/
public function getName( ) {
return 'evaluate';
}
}
Example usage:
$twig_environment->addExtension( new EvaluateExtension( ) );
In the template:
{% set var = 'inner variable' %}
{{'this is a string with an {{var}}'|evaluate}}
See http://twig.sensiolabs.org/doc/functions/template_from_string.html
It seems that this is frequently missed as most folks think (and search for) "eval" when expecting a filter/function to evaluate in the current language they're drafting in. Template from string isn't the first search query that comes to mind.
One option is to render your templates as strings. You can do that like this:
$env = new \Twig_Environment(new \Twig_Loader_String());
echo $env->render(
"Hello {{ name }}",
array("name" => "World")
);
I'll leave it to you to decide how exactly to structure your code to make this work, but it might go something like this:
1) Fetch the inner template text that contains the variables that aren't being replaced.
2) Render that inner template text into an $html variable. Be sure to pass in any vars you need.
3) Render your original template that contains {{html}}. Be sure to pass in 'html' => $html in the vars array
You can also pass an array or an object to the view, and then use the twig attribute() method: http://twig.sensiolabs.org/doc/functions/attribute.html
{% if attribute(array, key) is defined %}
{{ attribute(array, key) }}
{% endif %}