How to export datasource type as array in datagrid, Orocommerce - datagrid

I created an array-based datagrid from datagrid.yml, but I don't know how to export the datagrid's data to a csv or excel file.
I'm using the onBuildAfter method in the listener to stuff the array data into the datagrid.
datagrid.yml:
test-demo-lookup-grid:
options:
export:
csv: { label: oro.grid.export.csv }
source:
type: array
columns:
id:
label: 'ID'
ot:
label: 'OT'
listener:
public function onBuildAfter(BuildAfter $event)
{
$datagrid = $event->getDatagrid();
$datasource = $datagrid->getDatasource();
if (!$datasource instanceof ArrayDatasource) {
throw new UnexpectedTypeException($datasource, ArrayDatasource::class);
}
$recordsJsonObj = $this->getParameter($datagrid, 'records');
if (!empty($recordsJsonObj)) {
$records = json_decode($recordsJsonObj);
$datasource->setArraySource($records);
}
}
enter code here

Related

GraphQL.NET: How to separate the root query into multiple parts

I currently have a small application which is using GraphQL to communicate with the .net core backend. I currently have one one root query as is mandatory for GraphQL and am looking for a way to break this up into multiple pieces for organization's sake. My Query looks as follows:
public class ReactToFactsQuery : ObjectGraphType
{
public ReactToFactsQuery(IArticleService articleService,
INewsItemService newsItemService)
{
Field<ArticleType>(
name: "article",
arguments: new QueryArguments(new QueryArgument<IntGraphType> { Name = "id" }),
resolve: context =>
{
var id = context.GetArgument<int>("id");
return articleService.Get(id);
}
);
Field<ListGraphType<ArticleType>>(
name: "articles",
arguments: new QueryArguments(new QueryArgument<IntGraphType>() { Name = "count" }),
resolve: context =>
{
var count = context.GetArgument<int?>("count");
if (count.HasValue)
{
return articleService.GetAll(count.Value);
}
else
{
return articleService.GetAll();
}
}
);
Field<ListGraphType<NewsItemType>>(
name: "newsItems",
arguments: new QueryArguments(
new QueryArgument<IntGraphType>() { Name = "count" },
new QueryArgument<IntGraphType>() { Name = "newsType" }),
resolve: context =>
{
var count = context.GetArgument<int?>("count");
var category = context.GetArgument<int>("newsType");
var newsType = (NewsType)category;
if (count.HasValue)
{
return newsItemService.GetMostRecent(newsType, count.Value);
}
else
{
return newsItemService.GetMostRecent(newsType);
}
}
);
}
}
Currently the query is pretty small and manageable but as the application grows, I can easily see there being a huge number of queries defined in this class. THe current query names that exist are article, articles, and newsItems. Preferably, I'd like to create a query class to represent each model type (i.e one query class for article related queries, one for news item related queries, etc).
I've read the documentation here however I for whatever reason am struggling to understand the example here and how to apply it to my code.
All help is appreciated.
As the documentation says, you can split queries into virtual groups like this ...
Creating sub query types (ArticlesQueryType) that controls the specific queries.
public class RootQuery : ObjectGraphType
{
public RootQuery()
{
Name = "RootQuery";
// defines the articles sub query and returns an empty anonymous type object
// whose only purpose is to allow making queries on the subtype (ArticlesQueryType)
Field<ArticlesQueryType>("articles", resolve: context => new {});
}
}
// defines the articles specific queries
public class ArticlesQueryType: ObjectGraphType
{
public ArticlesQueryType(IArticleService articleService)
{
Name = "ArticlesQuery";
Field<ArticleType>(
name: "article",
arguments: new QueryArguments(new QueryArgument<IntGraphType> { Name = "id" }),
resolve: context =>
{
var id = context.GetArgument<int>("id");
return articleService.Get(id);
});
}
}
GraphQL query type would be
type RootQuery {
articles: ArticlesQuery
news: NewsQuery
}
type ArticlesQuery {
article(id: ID): Article
articles: [Article]
}
...
On the other hand, if you don't want to change the query structure and has only one root that holds the specific queries, you can split the queries into partial classes for clarity ...
public partial class RootQuery: ObjectGraphType
{
private IArticleService ArticleService { get; }
public RootQuery()
{
Name = "RootQuery";
InitializeArticlesQueries()
}
}
and in another file (RootQuery_Articles.cs) for example
public partial class RootQuery
{
protected InitializeArticlesQuery()
{
Field<ArticleType>(
name: "article",
arguments: new QueryArguments(new QueryArgument<IntGraphType> { Name = "id" }),
resolve: context =>
{
var id = context.GetArgument<int>("id");
return articleService.Get(id);
});
}
}
This way, the GraphQL query type is
type RootQuery {
articles: [Article]
....
}

Displaying name and selecting id on select for Twitter Typeahead

I am trying to implement twitter typeahead into my project, having remote as the source. I am able to make the connection between the front end query text and the sql. The return response looks like this:
[
{
id: 1,
name: 'user one'
},
{
id: 2,
name: 'user two'
}
..
]
The typeahead displays the matching items but it includes the id along with the names in the selection, instead of just the name. Second, I want to get the id value on select but the :select always gives the name value instead of the id.
here is my code:
var source = new Bloodhound({
datumTokenizer: Bloodhound.tokenizers.obj.whitespace('name'),
queryTokenizer: Bloodhound.tokenizers.whitespace,
remote: {
url:"{{ path('user_typeahead') }}"+'?string=%QUERY', // twig path
wildcard: '%QUERY',
filter: function (results) {
// Map the remote source JSON array to a JavaScript object array
return $.map(results, function (result) {
return {
value: result
};
});
}
}
});
// Initialize the Bloodhound suggestion engine
source.initialize();
$('#typeahead').typeahead(null, {
display: 'value',
source: source.ttAdapter(),
limit:5,
highlight: true,
hint: true
});
$('#typeahead').bind('typeahead:select', function(ev, suggestion) {
console.log(suggestion);
});
Try this,
Display should be set to the property of the json object. display: 'id'
Chain the custom event. Access your id from suggestion.id.
$('#typeahead').typeahead(null, {
display: 'id',
source: source.ttAdapter(),
limit:5,
highlight: true,
hint: true
}).bind('typeahead:select', function(ev, suggestion) {
console.log(suggestion);
});

Data in one component wont bind to array in injectable

I have following issue:
I have one component, in which I am calling:
this.users = UsersInj.getUsersCollection()
In UsersInj, I have:
#Injectable()
export class UsersInj{
public users:any = [];
constructor(private _http:Http){
this.getUsers().subscribe(
success=>{
this.users = success.json();
},
error =>{
console.log('error')
}
)
}
getUsers(){
return this._http.get('/api/user');
}
getUsersCollection(){
console.log('GET USERS COLLECTION :',this.users);
return this.users;
}
}
However, this.users.length in my component is always 0. Any ideas?
UPDATE
It works when I pack this.users in UsersInj in an object.
PLNKR
In the plunker you copy the values (references) once when TheContent is created.
export class TheContent {
name: any;
constructor(public nameService: NameService) {
console.log("content started");
this.info = nameService.info
this.names = nameService.names;
}
changeMyName() {
this.nameService.change();
}
}
In NameService you assign a new array to this.names.
this.names = success.json();
this.names in TheContent and this.names in NameService are now not connected anymore.
What you probably want to do is
change(){
this.info.name = "Jane";
this.http.get('https://api.github.com/repos/vmg/redcarpet/issues?state=closed').subscribe(success=>{
while(this.names.length > 0) {
this.names.pop();
}
this.names.push.apply(this.names, success.json());
console.log('names: ' + this.names);
});
}
or alternatively copy the new array to TheContent again.
In Angular using an Observable that allows interested parties to subscribe to changes and pass the new value with the notification is the preferred way. See also https://stackoverflow.com/a/35568924/217408

How to use aurelia-validate with a object properties to validate?

I'm using aurelia-validate and my validation works fine if I use variables, but I need it to validate properties of an object rather than a variable:
Here's what works:
import {Validation} from 'aurelia-validation';
import {ensure} from 'aurelia-validation';
import {ItemService} from './service';
export class EditItem {
static inject() {
return [Validation, ItemService];
}
#ensure(function(it){
it.isNotEmpty()
.hasLengthBetween(3,10);
})
name = '';
#ensure(function(it){
it.isNotEmpty()
.hasMinLength(10)
.matches(/^https?:\/\/.{3,}$/) //looks like a url
.matches(/^\S*$/); //no spaces
})
url = '';
constructor(validation, service) {
this.validation = validation.on(this);
this.service = service;
}
activate(params){
return this.service.getItem(params.id).then(res => {
console.log(res);
this.name = res.content.name; //populate
this.url = res.content.url;
});
}
update() {
this.validation.validate().then(
() => {
var data = {
name: this.name,
url: this.url
};
this.service.updateItem(data).then(res => {
this.message = "Thank you!";
})
}
);
}
}
Here's what I'm trying to do (but doesn't work)...also I'm not sure if it's better to keep the properties on the class or have a property called this.item which contains the properties (this is the typical angular way):
import {Validation} from 'aurelia-validation';
import {ensure} from 'aurelia-validation';
import {ItemService} from './service';
export class EditItem {
static inject() {
return [Validation, ItemService];
}
#ensure(function(it){
it.isNotEmpty()
.hasLengthBetween(3,10);
})
this.item.name; //no assignment here should happen
#ensure(function(it){
it.isNotEmpty()
.hasMinLength(10)
.matches(/^https?:\/\/.{3,}$/) //looks like a url
.matches(/^\S*$/); //no spaces
})
this.item.url; //no assignment?
constructor(validation, service) {
this.validation = validation.on(this);
this.service = service;
this.item = null;
}
activate(params){
return this.service.getItem(params.id).then(res => {
console.log(res);
this.item = res.content; //populate with object from api call
});
}
update() {
this.validation.validate().then(
() => {
var data = {
name: this.item.name,
url: this.item.url
};
this.service.updateItem(data).then(res => {
this.message = "Thank you!";
})
}
);
}
}
Can someone give me some guidance here on how to use a validator against an existing object (for an edit page)?
The validation works in all kinds of situations, but using the #ensure decorator can only be used to declare your rules on simple properties (like you found out).
Hence...
Option a: replace the ensure decorator with the fluent API 'ensure' method, this supports 'nested' or 'complex' binding paths such as:
import {Validation} from 'aurelia-validation';
import {ItemService} from './service';
export class EditItem {
static inject() {
return [Validation, ItemService];
}
constructor(validation, service) {
this.validation = validation.on(this)
.ensure('item.url')
.isNotEmpty()
.hasMinLength(10)
.matches(/^https?:\/\/.{3,}$/) //looks like a url
.matches(/^\S*$/)
.ensure('item.name')
.isNotEmpty()
.hasLengthBetween(3,10);
this.service = service;
this.item = null;
}
activate(params){
return this.service.getItem(params.id).then(res => {
console.log(res);
this.item = res.content; //populate with object from api call
});
}
update() {
this.validation.validate().then(
() => {
var data = {
name: this.item.name,
url: this.item.url
};
this.service.updateItem(data).then(res => {
this.message = "Thank you!";
})
}
);
}
}
Note: you can set up your validation even before item is set. Cool, no?
Option b: Since the validation rules are specific to the item, you could move your validation rules inside your item class using the #ensure decorator inside that class instead.
You can then set up validation in your VM after you've retrieved the item: this.validation = validation.on(this.item); or, your service can set up the validation when it returns your item to your VM and make it an intrinsic part of the model: item.validation = validation.on(item);
Option a is easiest and seems to match your experience. Option b is more maintainable, as the validation rules for your model will live on the model, not on the view-model. However if you go with option b, you might have to adjust your HTML a bit to make sure validation hints appear.
Use the .on method of the validator to apply your rules to object properties.
The example below is called after I retrieve an object named stock, it validates that the quantity is not empty and is numeric only. Hope this helps...
let stock = {
name: 'some name'
minimumQuantity: '1'
};
applyRules() {
ValidationRules
.ensure((m: EditStock) => m.minimumQuantity)
.displayName("Minimum Quantity")
.required()
.withMessage(`\${$displayName} cannot be blank.`)
.matches( /^[0-9]*$/)
.withMessage(`\${$displayName} must be numeric only.`)
.on(this.stock);
}

Symfony add Avatar field to sfGuardUser model

I have a project in symfony that I would like to let my users upload an image for their "avatar" field. I have found many posts about how to "extend" the table which I have with the schema below:
Member:
inheritance:
type: column_aggregation
extends: sfGuardUser
columns:
idmember: { type: integer }
birthday: { type: date }
avatar: { type: string(255) }
bio: { type: string(255) }
The columns get added to the table just fine, but when I go to change the widget to a sfWidgetFormInputFileEditable it breaks. Here is the Form.class file:
$file_src = $this->getObject()->getAvatar();
if ($file_src == '')
{
$file_src = 'default_image.png';
}
$this->widgetSchema['avatar'] = new sfWidgetFormInputFileEditable(array(
'label' => ' ',
'file_src' => '/uploads/avatar/'.$file_src,
'is_image' => true,
'edit_mode' => true,
'template' => '<div>%file%<br />%input%</div>',
));
and "save" function of the form:
if($this->isModified())
{
$uploadDir = sfConfig::get('sf_upload_dir');
$thumbnail = new sfThumbnail(150, 150);
$thumbnail2 = new sfThumbnail(800, 800);
if($this->getAvatar())
{
$thumbnail->loadFile($uploadDir.'/avatar/'.$this->getAvatar());
$thumbnail->save($uploadDir.'/avatar/thumbnail/'. $this->getAvatar());
$thumbnail2->loadFile($uploadDir.'/avatar/'.$this->getAvatar());
$thumbnail2->save($uploadDir.'/avatar/big/'. $this->getAvatar());
}
}
When I submit the form, I get this error message:
This form is multipart, which means you need to supply a files array as the bind() method second argument.
In the action where you bind the form you should use something like this:
$form->bind($request->getParamater($form->getName()), $request->getFiles($form->getName()));
So you need to pass the uploaded files as the second parameter to the bind method.

Resources