How to extend ToJson from persistent model - yesod

I'd like to extend the default ToJson instance of my model:
# models
Event
title Text
content Text
deriving Eq
deriving Show
How would I go about:
Adding the id to the ToJson
Can I somehow get the default one, and
just extend it
I have tried to manually define my ToJson/FromJson but when I do:
getEventsR :: Handler Value
getEventsR = do
events <- runDB $ selectList [] [ Desc EventId, LimitTo 5 ] :: Handler [Entity Event]
return $ object ["data" .= events]
I get the following error:
Handler/Events.hs:10:29:
No instance for (Foldable Entity) arising from a use of ‘.=’
In the expression: "data" .= events
In the first argument of ‘object’, namely ‘["data" .= events]’
In the second argument of ‘($)’, namely ‘object ["data" .= events]’

Related

It is possible to somehow use forward_missing_to to fetch/set hash values?

I am building client for Asterisk Manager (AMI), which sending back "events" (strings in key-value format). Depending on event type the keys are different. Some known would be defined similar as a property, but I also want to access everything else similar way (I need only getter).
Below is example and I am confused about forward_missing_to, to fetch hash value (i.e. e.foo => e.data["foo"] and e.foo? => e.data["foo"]?
class Event
#data = Hash(String,String).new
def initialize(data : Hash(String, String))
#data.merge! data
end
# known field (event), here I could apply various validations etc.
def event=(#name : String)
#data["name"] = #name
end
def event : String
#data["name"].not_nil!
end
# Confusing about this:
forward_missing_to #data
end
Example of usage:
e = Event.new({"event" => "Test", "foo" => "bar"})
p e.event # => "Test" (only this works)
p e.foo # => expecting to get "bar"
p e.unknown # => should throw Unhandled exception: Missing hash key: "unknown" (KeyError)
p e.unknown? # => nil
forward_missing_to just means this:
e = Event.new({"event" => "Test", "foo" => "bar"})
# Same as invoking `foo` on the #data instance variable
# because you said forward_missing_to #data
e.foo
It doesn't mean invoking ["foo"] on #data.
You can do it like this:
macro method_missing(call)
{% if call.args.empty? && !call.named_args %}
#data[{{call.name.stringify}}]
{% else %}
{% raise "undefined method '#{call.name}' for #{#type}" %}
{% end %}
end
However, I wouldn't advise using method_missing, mainly because it might be eventually removed from the language (together with forward_missing_to).

Flow: Convert object to array for CSV export

I want to export my object "mitglied" to a .csv-file. My controller looks like that:
public function exportAction() {
// find all mitglieds
$records = $this->mitgliedRepository->findTennis();
// Set path for export-file
$csvPath = '/var/www/apps/flow/Packages/Application/ITOOP.Atc/Resources/Private/Export/test.csv';
$fp = fopen($csvPath, 'w');
foreach ($records as $lines) {
fputcsv($fp, $lines);
}
fclose($fp);
}
When I call the exportAction, I get a an error:
#1: Warning: fputcsv() expects parameter 2 to be array, object given in /var/www/apps/flow/Data/Temporary/Development/Cache/Code/Flow_Object_Classes/itoop_atc_Controller_MitgliedController.php line 494
line 494 is...
fputcsv($fp, $lines);
...so I think I have to convert the object "mitglied" to an array.
My the public function findTennis in my mitgliedRepository looks like that:
public function findTennis() {
$query = $this->createQuery();
$result = $query->matching($query->equals('abteilung', 'Tennis'))
->setOrderings(array('name' => \TYPO3\Flow\Persistence\QueryInterface::ORDER_ASCENDING))
->execute();
return $result;
}
I tried to set toArray(); in the repository like the that:
public function findTennis() {
$query = $this->createQuery();
$result = $query->matching($query->equals('abteilung', 'Tennis'))
->setOrderings(array('name' => \TYPO3\Flow\Persistence\QueryInterface::ORDER_ASCENDING))
->execute()
->toArray;
return $result;
}
But then I get the following error:
#1: Notice: Undefined property: TYPO3\Flow\Persistence\Doctrine\QueryResult::$toArray in /var/www/apps/flow/Data/Temporary/Development/Cache/Code/Flow_Object_Classes/itoop_atc_Domain_Repository_MitgliedRepository.php line 105
line 105 of course is
->toArray;
Does anybody know, how to convert an object to an array in flow?
With the following example the export works, so I think the (formatting of the) repository query is the problem.
public function exportAction() {
// Set path for export-file
$csvPath = '/var/www/apps/flow/Packages/Application/ITOOP.Atc/Resources/Private/Export/test.csv';
$test = array (
array('xxx', 'bbb', 'ccc', 'dddd'),
array('123', '456', '789'),
array('aaa', 'bbb')
);
$fp = fopen($csvPath, 'w');
foreach ($test as $lines) {
fputcsv($fp, $lines);
}
fclose($fp);
}
Please point me to the right direction. Thank you!
The error messages explained
#1: Warning: fputcsv() expects parameter 2 to be array, object given in /var/www/apps/flow/Data/Temporary/Development/Cache/Code/Flow_Object_Classes/itoop_atc_Controller_MitgliedController.php line 494
fputcsv expects it's 2nd parameter to be an array. That array will be written as a CSV line into single file, with each array element as column. When iterating over your $records variable, you get instances of your domain object class (so probably sth. like ITOOP\Atc\Domain\Model\Mitglied). That's undefined behaviour, thus the warning.
#1: Notice: Undefined property: TYPO3\Flow\Persistence\Doctrine\QueryResult::$toArray in /var/www/apps/flow/Data/Temporary/Development/Cache/Code/Flow_Object_Classes/itoop_atc_Domain_Repository_MitgliedRepository.php line 105
toArray is a function that is offered by Doctrine QueryResult class. Typically, Doctrine queries do not fetch all objects returned by the query, but return an iterator that fetches and maps entities on-demand. The toArray method fetches all records at once and returns an array instead of the iterator. Your error occurs, because you try to access toArray as a property, and not calling it as a method. The following code would be correct:
$result = $query->matching($query->equals('abteilung', 'Tennis'))
->setOrderings(array('name' => \TYPO3\Flow\Persistence\QueryInterface::ORDER_ASCENDING))
->execute()
->toArray(); // <- Mind the brackets!
However, this will not help you anything, because in your controller, you will still be iterating over a list of domain entities (foreach does not care if its iterating over an iterator or an array; that's actually the point of iterators in PHP).
Quick&Dirty solution
Convert your domain entities by hand in your controller. Only you can know how your CSV export should look like, so this cannot be automated. I'm thinking something like this:
foreach ($records as $record) {
$csvLine = [
$record->getFirstProperty(),
$record->getSecondProperty(),
// and so on...
];
fputcsv($fp, $csvLine);
}
Better solution
Rendering CSV data is not a concern that should be addressed in the controller. Basically, it should go into a view. You can implement a custom view class for handling the CSV output.
For that, you need to implement the \TYPO3\Flow\Mvc\View\ViewInterface. The easiest way to do this is to subclass \TYPO3\Flow\Mvc\View\AbstractView. Name your view class <PackageNamespace>\View\<Controller>\Action<Format> (so sth. like ITOOP\Atc\View\Mitglied\ExportCsv. Implement your CSV export logic in the view's render() method. Flow will pick up and use the view class automatically as soon as it's present.
Implementing custom views is explained in depth in this article -- it's in German though, although based on your class naming I suspect that won't be a problem ;).
I solved the problem with arbitrary DQL. As I mentioned I think the problem was that I didn't got an array as result by the query. But with the following query in my repository I do:
/**
* #Flow\Inject
* #var \Doctrine\Common\Persistence\ObjectManager
* inject Doctrine's EntityManager to execute arbitrary DQL
*/
protected $entityManager;
/**
* find mitglieder with Abteilung Tennis und return an array
*/
public function exportTennis() {
$query = $this->entityManager->createQuery("SELECT mitglied FROM \itoop\atc\Domain\Model\Mitglied mitglied WHERE mitglied.abteilung = 'Tennis'");
return $query->getResult(\Doctrine\ORM\Query::HYDRATE_ARRAY);
}
The important part I think is getResult(\Doctrine\ORM\Query::HYDRATE_ARRAY);

Accessing properties in templates by their name

given the following pieces of code:
groovy:
binding = [key1: "val1"]
def f = new File('test.template')
engine = new GStringTemplateEngine()
template = engine.createTemplate(f).make(binding)
println template.toString()
test.template:
<% keyName = "key1" %>
Is there a way to access val1 by keyName in test.template?
This:
${ binding[keyName] }
does not work (No such property: key1 for class: groovy.lang.Binding). Any ideas? Maybe the name of the map holding the properties is different?
I know I could just write:
${ key1 }
but I need to access property key1 using variable keyName.
Not sure if this is better but I got the following to work (somewhat)
Map binding = [ keyName: 'key1', key1: "val1", m: [key1:'val100', key2:'val2']]
def f = new File('test.template')
def engine = new groovy.text.GStringTemplateEngine()
def template = engine.createTemplate(f).make(binding)
println template.toString()
with the following template:
$keyName
$key1
<%= m[keyName] %>
But this relies on a submap that holds the values you are looking for.
I can see scenarios where in the binding, you pass a list of fields you want to process or display (rather than knowing them ahead of time), so you would have to get the field names from a well-known variable and then process the others possibly thru a submap.

How to use template design pattern in Powershell?

I want to create a server smo object in my function, then use it do something useful with the passed in scriptblock. After that, the server veriable will be removed. I want to design my function something similar to a template design pattern implementation. My code is listed below, I'm not sure whether how to use the $server variable in the scriptblock. Any one can help? Thanks.
function test{
[CmdletBinding()]
param (
[Parameter(Mandatory = $true, Position = 0)]
[object]
$instance,
[Parameter(Mandatory = $true, Position = 1)]
[scriptblock]
$script
)
[Reflection.Assembly]::LoadWithPartialName('Microsoft.SqlServer.SMO') | out-null
$server = new-object ('Microsoft.SqlServer.Management.Smo.Server') $instance
# do something with $script
Remove-Variable -Name $server
}
The scriptblock needs to be written such that it is expecting a server variable e.g.:
test $anInstance {param($server) $server.DoSomething}
Then in your test function execute the scriptblock like so:
& $scripblock $server
And if the scriptblock needs multiple parameters:
test $anInstance {param($server, $name) $server.DoSomething}
Remember to invoke using space separated args:
& $scripblock $server "A name"

Symfony2, Doctrine 2: getResult Object

$posts = $em->find('Application\BlogBundle\Entity\Post',1);
print_r ($posts);
Why I got it?
Barii\BlogBundle\Entity\Post Object ( [id:Barii\BlogBundle\Entity\Post:private] => 1 [title:Application\BlogBundle\Entity\Post:private] => something [body:Application\BlogBundle\Entity\Post:private] => content )
instead of a simple array like this:
array ( [id] => 1,
[title] => "something",
[body] => "content" )
I use it with Symfony 2.
You have a couple options here. As far as I know, you can't find results as arrays from entity repositories by default. Instead, you can do one of two things:
First, you could implement a toArray() method on your entity object (perhaps through a mapped superclass) that simply returns an array of properties.
Second, you could use Doctrine Query Language to pull the information that you need using the getArrayResult() method, perhaps something like this:
$query = $em->createQuery('SELECT p FROM Application\BlogBundle\Entity\Post p WHERE p.id=:pid');
$query->setParameter('tid', $postId);
$result = $query->getArrayResult(); // shortcut for $query->getResult(Query::HYDRATE_ARRAY);
More in-depth documentation on DQL can be found here.