Listing & form

- improved Listing in order to make it more DRY
- form builder with all field types
- added support for data array in ValidModel->set()
- updated models to comply with Listing & Form methods
This commit is contained in:
Jonathan Labreuille
2015-09-14 19:07:41 +02:00
parent e471d45827
commit 79f1896cf3
19 changed files with 451 additions and 272 deletions

View File

@ -1,5 +1,5 @@
.mailpoet_listing_loading tbody tr, .mailpoet_listing_loading tbody tr,
.mailpoet_form_loading .mailpoet_form_loading tbody tr
opacity: 0.2; opacity: 0.2;
.widefat tfoot td.mailpoet_check_column, .widefat tfoot td.mailpoet_check_column,

View File

@ -3,17 +3,191 @@ define(
'react', 'react',
'mailpoet', 'mailpoet',
'classnames', 'classnames',
'react-router' 'react-router',
'react-checkbox-group'
], ],
function( function(
React, React,
MailPoet, MailPoet,
classNames, classNames,
Router Router,
CheckboxGroup
) { ) {
var FormFieldSelect = React.createClass({
render: function() {
var options =
Object.keys(this.props.field.values).map(function(value, index) {
return (
<option
key={ 'option-' + index }
value={ value }>
{ this.props.field.values[value] }
</option>
);
}.bind(this)
);
return (
<select
name={ this.props.field.name }
id={ 'field_'+this.props.field.name }
value={ this.props.item[this.props.field.name] }
onChange={ this.props.onValueChange } >
{options}
</select>
);
}
});
var FormFieldRadio = React.createClass({
render: function() {
var selected_value = this.props.item[this.props.field.name];
var count = Object.keys(this.props.field.values).length;
var options = Object.keys(this.props.field.values).map(
function(value, index) {
return (
<p key={ 'radio-' + index }>
<label>
<input
type="radio"
checked={ selected_value === value }
value={ value }
onChange={ this.props.onValueChange }
name={ this.props.field.name } />
&nbsp;{ this.props.field.values[value] }
</label>
</p>
);
}.bind(this)
);
return (
<div>
{ options }
</div>
);
}
});
var FormFieldCheckbox = React.createClass({
render: function() {
var selected_values = this.props.item[this.props.field.name] || '';
if(
selected_values !== undefined
&& selected_values.constructor !== Array
) {
selected_values = selected_values.split(';').map(function(value) {
return value.trim();
});
}
var count = Object.keys(this.props.field.values).length;
var options = Object.keys(this.props.field.values).map(
function(value, index) {
return (
<p key={ 'checkbox-' + index }>
<label>
<input type="checkbox" value={ value } />
&nbsp;{ this.props.field.values[value] }
</label>
</p>
);
}.bind(this)
);
return (
<CheckboxGroup
name={ this.props.field.name }
value={ selected_values }
ref={ this.props.field.name }
onChange={ this.handleValueChange }>
{ options }
</CheckboxGroup>
);
},
handleValueChange: function() {
var field = this.props.field.name;
var group = this.refs[field];
var selected_values = [];
if(group !== undefined) {
selected_values = group.getCheckedValues();
}
return this.props.onValueChange({
target: {
name: field,
value: selected_values.join(';')
}
});
}
});
var FormFieldText = React.createClass({
render: function() {
return (
<input
type="text"
className="regular-text"
name={ this.props.field.name }
id={ 'field_'+this.props.field.name }
value={ this.props.item[this.props.field.name] }
onChange={ this.props.onValueChange } />
);
}
});
var FormFieldTextarea = React.createClass({
render: function() {
return (
<textarea
type="text"
className="regular-text"
name={ this.props.field.name }
id={ 'field_'+this.props.field.name }
value={ this.props.item[this.props.field.name] }
onChange={ this.props.onValueChange } />
);
}
});
var FormField = React.createClass({ var FormField = React.createClass({
render: function() { render: function() {
var description = false;
if(this.props.field.description) {
description = (
<p className="description">{ this.props.field.description }</p>
);
}
var field = false;
switch(this.props.field.type) {
case 'text':
field = (<FormFieldText {...this.props} />);
break;
case 'textarea':
field = (<FormFieldTextarea {...this.props} />);
break;
case 'select':
field = (<FormFieldSelect {...this.props} />);
break;
case 'radio':
field = (<FormFieldRadio {...this.props} />);
break;
case 'checkbox':
field = (<FormFieldCheckbox {...this.props} />);
break;
}
return ( return (
<tr> <tr>
<th scope="row"> <th scope="row">
@ -22,12 +196,8 @@ define(
>{ this.props.field.label }</label> >{ this.props.field.label }</label>
</th> </th>
<td> <td>
<input { field }
type="text" { description }
name={ this.props.field.name }
id={ 'field_'+this.props.field.name }
value={ this.props.item[this.props.field.name] }
onChange={ this.props.onValueChange } />
</td> </td>
</tr> </tr>
); );
@ -66,7 +236,7 @@ define(
this.setState({ loading: true }); this.setState({ loading: true });
MailPoet.Ajax.post({ MailPoet.Ajax.post({
endpoint: 'subscribers', endpoint: this.props.endpoint,
action: 'get', action: 'get',
data: { id: id } data: { id: id }
}).done(function(response) { }).done(function(response) {
@ -91,7 +261,7 @@ define(
this.setState({ loading: true }); this.setState({ loading: true });
MailPoet.Ajax.post({ MailPoet.Ajax.post({
endpoint: 'subscribers', endpoint: this.props.endpoint,
action: 'save', action: 'save',
data: this.state.item data: this.state.item
}).done(function(response) { }).done(function(response) {
@ -100,19 +270,21 @@ define(
if(response === true) { if(response === true) {
this.transitionTo('/'); this.transitionTo('/');
if(this.props.params.id !== undefined) { if(this.props.params.id !== undefined) {
MailPoet.Notice.success('Subscriber succesfully updated!'); this.props.messages['updated']();
} else { } else {
MailPoet.Notice.success('Subscriber succesfully added!'); this.props.messages['created']();
} }
} else { } else {
this.setState({ errors: response }); this.setState({ errors: response });
} }
}.bind(this)); }.bind(this));
}, },
handleValueChange: function(e) { handleValueChange: function(e) {
var item = this.state.item; var item = this.state.item,
item[e.target.name] = e.target.value; field = e.target.name;
item[field] = e.target.value;
this.setState({ this.setState({
item: item item: item
}); });
@ -121,7 +293,9 @@ define(
render: function() { render: function() {
var errors = this.state.errors.map(function(error, index) { var errors = this.state.errors.map(function(error, index) {
return ( return (
<p key={'error-'+index} className="mailpoet_error">{ error }</p> <p key={ 'error-'+index } className="mailpoet_error">
{ error }
</p>
); );
}); });

View File

@ -159,7 +159,30 @@ define(
}, },
getItems: function() { getItems: function() {
this.setState({ loading: true }); this.setState({ loading: true });
this.props.items.bind(null, this)();
MailPoet.Ajax.post({
endpoint: this.props.endpoint,
action: 'listing',
data: {
offset: (this.state.page - 1) * this.state.limit,
limit: this.state.limit,
group: this.state.group,
search: this.state.search,
sort_by: this.state.sort_by,
sort_order: this.state.sort_order
},
onSuccess: function(response) {
if(this.isMounted()) {
this.setState({
items: response.items || [],
filters: response.filters || [],
groups: response.groups || [],
count: response.count || 0,
loading: false
});
}
}.bind(this)
});
}, },
handleSearch: function(search) { handleSearch: function(search) {
this.setState({ this.setState({

View File

@ -1,75 +1,50 @@
define( define(
[ [
'react', 'react',
'react-router', 'mailpoet',
'jquery', 'form/form.jsx'
'mailpoet'
], ],
function( function(
React, React,
Router, MailPoet,
jQuery, Form
MailPoet
) { ) {
var Form = React.createClass({ var fields = [
mixins: [ {
Router.Navigation name: 'subject',
], label: 'Subject',
getInitialState: function() { type: 'text'
return {
loading: false,
errors: []
};
}, },
handleSubmit: function(e) { {
e.preventDefault(); name: 'body',
label: 'Body',
type: 'textarea'
}
];
this.setState({ loading: true }); var messages = {
updated: function() {
MailPoet.Ajax.post({ MailPoet.Notice.success('Newsletter succesfully updated!');
endpoint: 'newsletters',
action: 'save',
data: {
subject: React.findDOMNode(this.refs.subject).value,
body: React.findDOMNode(this.refs.body).value
}
}).done(function(response) {
this.setState({ loading: false });
if(response === true) {
this.transitionTo('/');
} else {
this.setState({ errors: response });
}
}.bind(this));
}, },
created: function() {
MailPoet.Notice.success('Newsletter succesfully added!');
}
};
var NewsletterForm = React.createClass({
render: function() { render: function() {
var errors = this.state.errors.map(function(error, index) {
return (
<p key={'error-'+index} className="mailpoet_error">{ error }</p>
);
});
return ( return (
<form onSubmit={ this.handleSubmit }> <Form
{ errors } endpoint="newsletters"
<p> fields={ fields }
<input type="text" placeholder="Subject" ref="subject" /> params={ this.props.params }
</p> messages={ messages } />
<p>
<input type="text" placeholder="Body" ref="body" />
</p>
<input
className="button button-primary"
type="submit"
value="Save"
disabled={this.state.loading} />
</form>
); );
} }
}); });
return Form; return NewsletterForm;
} }
); );

View File

@ -1,18 +1,17 @@
define( define(
[ [
'react', 'react',
'jquery', 'react-router',
'mailpoet',
'listing/listing.jsx', 'listing/listing.jsx',
'classnames' 'classnames',
], ],
function( function(
React, React,
jQuery, Router,
MailPoet,
Listing, Listing,
classNames classNames
) { ) {
var Link = Router.Link;
var columns = [ var columns = [
{ {
@ -32,32 +31,7 @@ define(
} }
]; ];
var List = React.createClass({ var NewsletterList = React.createClass({
getItems: function(listing) {
MailPoet.Ajax.post({
endpoint: 'newsletters',
action: 'get',
data: {
offset: (listing.state.page - 1) * listing.state.limit,
limit: listing.state.limit,
group: listing.state.group,
search: listing.state.search,
sort_by: listing.state.sort_by,
sort_order: listing.state.sort_order
},
onSuccess: function(response) {
if(listing.isMounted()) {
listing.setState({
items: response.items || [],
filters: response.filters || [],
groups: response.groups || [],
count: response.count || 0,
loading: false
});
}
}.bind(listing)
});
},
renderItem: function(newsletter) { renderItem: function(newsletter) {
var rowClasses = classNames( var rowClasses = classNames(
'manage-column', 'manage-column',
@ -71,6 +45,12 @@ define(
<strong> <strong>
<a>{ newsletter.subject }</a> <a>{ newsletter.subject }</a>
</strong> </strong>
<div className="row-actions">
<span className="edit">
<Link to="edit" params={{ id: newsletter.id }}>Edit</Link>
</span>
</div>
</td> </td>
<td className="column-date" data-colname="Subscribed on"> <td className="column-date" data-colname="Subscribed on">
<abbr>{ newsletter.created_at }</abbr> <abbr>{ newsletter.created_at }</abbr>
@ -84,6 +64,7 @@ define(
render: function() { render: function() {
return ( return (
<Listing <Listing
endpoint="newsletters"
onRenderItem={this.renderItem} onRenderItem={this.renderItem}
items={this.getItems} items={this.getItems}
columns={columns} /> columns={columns} />
@ -91,6 +72,6 @@ define(
} }
}); });
return List; return NewsletterList;
} }
); );

View File

@ -2,20 +2,20 @@ define(
[ [
'react', 'react',
'react-router', 'react-router',
'newsletters/form.jsx', 'newsletters/list.jsx',
'newsletters/list.jsx' 'newsletters/form.jsx'
], ],
function( function(
React, React,
Router, Router,
Form, List,
List Form
) { ) {
var DefaultRoute = Router.DefaultRoute; var DefaultRoute = Router.DefaultRoute;
var Link = Router.Link; var Link = Router.Link;
var Route = Router.Route; var Route = Router.Route;
var RouteHandler = Router.RouteHandler; var RouteHandler = Router.RouteHandler;
var NotFoundRoute = Router.NotFoundRoute;
var App = React.createClass({ var App = React.createClass({
render: function() { render: function() {
@ -24,7 +24,7 @@ define(
<h1> <h1>
{ MailPoetI18n.pageTitle } { MailPoetI18n.pageTitle }
&nbsp; &nbsp;
<Link className="add-new-h2" to="form">New</Link> <Link className="add-new-h2" to="new">New</Link>
</h1> </h1>
<RouteHandler/> <RouteHandler/>
@ -35,8 +35,9 @@ define(
var routes = ( var routes = (
<Route name="app" path="/" handler={App}> <Route name="app" path="/" handler={App}>
<Route name="list" handler={List} /> <Route name="new" path="/new" handler={Form} />
<Route name="form" handler={Form} /> <Route name="edit" path="/edit/:id" handler={Form} />
<NotFoundRoute handler={List} />
<DefaultRoute handler={List} /> <DefaultRoute handler={List} />
</Route> </Route>
); );

View File

@ -1,71 +1,45 @@
define( define(
[ [
'react', 'react',
'react-router', 'mailpoet',
'jquery', 'form/form.jsx'
'mailpoet'
], ],
function( function(
React, React,
Router, MailPoet,
jQuery, Form
MailPoet
) { ) {
var Form = React.createClass({ var fields = [
mixins: [ {
Router.Navigation name: 'name',
], label: 'Name',
getInitialState: function() { type: 'text'
return { }
loading: false, ];
errors: []
}; var messages = {
updated: function() {
MailPoet.Notice.success('Segment succesfully updated!');
}, },
handleSubmit: function(e) { created: function() {
e.preventDefault(); MailPoet.Notice.success('Segment succesfully added!');
}
};
this.setState({ loading: true }); var SegmentForm = React.createClass({
MailPoet.Ajax.post({
endpoint: 'segments',
action: 'save',
data: {
name: React.findDOMNode(this.refs.name).value
}
}).done(function(response) {
this.setState({ loading: false });
if(response === true) {
this.transitionTo('/');
} else {
this.setState({ errors: response });
}
}.bind(this));
},
render: function() { render: function() {
var errors = this.state.errors.map(function(error, index) {
return (
<p key={'error-'+index} className="mailpoet_error">{ error }</p>
);
});
return ( return (
<form onSubmit={ this.handleSubmit }> <Form
{ errors } endpoint="segments"
<p> fields={ fields }
<input type="text" placeholder="Name" ref="name" /> params={ this.props.params }
</p> messages={ messages } />
<input
className="button button-primary"
type="submit"
value="Save"
disabled={this.state.loading} />
</form>
); );
} }
}); });
return Form; return SegmentForm;
} }
); );

View File

@ -1,18 +1,17 @@
define( define(
[ [
'react', 'react',
'jquery', 'react-router',
'mailpoet',
'listing/listing.jsx', 'listing/listing.jsx',
'classnames' 'classnames',
], ],
function( function(
React, React,
jQuery, Router,
MailPoet,
Listing, Listing,
classNames classNames
) { ) {
var Link = Router.Link;
var columns = [ var columns = [
{ {
@ -32,32 +31,7 @@ define(
} }
]; ];
var List = React.createClass({ var SegmentList = React.createClass({
getItems: function(listing) {
MailPoet.Ajax.post({
endpoint: 'segments',
action: 'get',
data: {
offset: (listing.state.page - 1) * listing.state.limit,
limit: listing.state.limit,
group: listing.state.group,
search: listing.state.search,
sort_by: listing.state.sort_by,
sort_order: listing.state.sort_order
},
onSuccess: function(response) {
if(listing.isMounted()) {
listing.setState({
items: response.items || [],
filters: response.filters || [],
groups: response.groups || [],
count: response.count || 0,
loading: false
});
}
}.bind(listing)
});
},
renderItem: function(segment) { renderItem: function(segment) {
var rowClasses = classNames( var rowClasses = classNames(
'manage-column', 'manage-column',
@ -71,6 +45,12 @@ define(
<strong> <strong>
<a>{ segment.name }</a> <a>{ segment.name }</a>
</strong> </strong>
<div className="row-actions">
<span className="edit">
<Link to="edit" params={{ id: segment.id }}>Edit</Link>
</span>
</div>
</td> </td>
<td className="column-date" data-colname="Subscribed on"> <td className="column-date" data-colname="Subscribed on">
<abbr>{ segment.created_at }</abbr> <abbr>{ segment.created_at }</abbr>
@ -84,6 +64,7 @@ define(
render: function() { render: function() {
return ( return (
<Listing <Listing
endpoint="segments"
onRenderItem={this.renderItem} onRenderItem={this.renderItem}
items={this.getItems} items={this.getItems}
columns={columns} /> columns={columns} />
@ -91,6 +72,6 @@ define(
} }
}); });
return List; return SegmentList;
} }
); );

View File

@ -11,11 +11,11 @@ define(
List, List,
Form Form
) { ) {
var DefaultRoute = Router.DefaultRoute; var DefaultRoute = Router.DefaultRoute;
var Link = Router.Link; var Link = Router.Link;
var Route = Router.Route; var Route = Router.Route;
var RouteHandler = Router.RouteHandler; var RouteHandler = Router.RouteHandler;
var NotFoundRoute = Router.NotFoundRoute;
var App = React.createClass({ var App = React.createClass({
render: function() { render: function() {
@ -24,7 +24,7 @@ define(
<h1> <h1>
{ MailPoetI18n.pageTitle } { MailPoetI18n.pageTitle }
&nbsp; &nbsp;
<Link className="add-new-h2" to="form">New</Link> <Link className="add-new-h2" to="new">New</Link>
</h1> </h1>
<RouteHandler/> <RouteHandler/>
@ -35,8 +35,9 @@ define(
var routes = ( var routes = (
<Route name="app" path="/" handler={App}> <Route name="app" path="/" handler={App}>
<Route name="list" handler={List} /> <Route name="new" path="/new" handler={Form} />
<Route name="form" handler={Form} /> <Route name="edit" path="/edit/:id" handler={Form} />
<NotFoundRoute handler={List} />
<DefaultRoute handler={List} /> <DefaultRoute handler={List} />
</Route> </Route>
); );

View File

@ -1,41 +1,61 @@
define( define(
[ [
'react', 'react',
'react-router',
'jquery',
'mailpoet', 'mailpoet',
'classnames',
'form/form.jsx' 'form/form.jsx'
], ],
function( function(
React, React,
Router,
jQuery,
MailPoet, MailPoet,
classNames,
Form Form
) { ) {
var fields = [ var fields = [
{ {
name: 'email', name: 'email',
label: 'E-mail' label: 'E-mail',
type: 'text'
}, },
{ {
name: 'first_name', name: 'first_name',
label: 'Firstname' label: 'Firstname',
type: 'text'
}, },
{ {
name: 'last_name', name: 'last_name',
label: 'Lastname' label: 'Lastname',
type: 'text'
},
{
name: 'status',
label: 'Status',
type: 'select',
values: {
'subscribed': 'Subscribed',
'unconfirmed': 'Unconfirmed',
'unsubscribed': 'Unsubscribed'
}
} }
]; ];
var messages = {
updated: function() {
MailPoet.Notice.success('Subscriber succesfully updated!');
},
created: function() {
MailPoet.Notice.success('Subscriber succesfully added!');
}
};
var SubscriberForm = React.createClass({ var SubscriberForm = React.createClass({
render: function() { render: function() {
return ( return (
<Form fields={ fields } params={ this.props.params } /> <Form
endpoint="subscribers"
fields={ fields }
params={ this.props.params }
messages={ messages } />
); );
} }
}); });

View File

@ -2,18 +2,15 @@ define(
[ [
'react', 'react',
'react-router', 'react-router',
'mailpoet',
'listing/listing.jsx', 'listing/listing.jsx',
'classnames', 'classnames',
], ],
function( function(
React, React,
Router, Router,
MailPoet,
Listing, Listing,
classNames classNames
) { ) {
var Link = Router.Link; var Link = Router.Link;
var columns = [ var columns = [
@ -65,31 +62,6 @@ define(
]; ];
var List = React.createClass({ var List = React.createClass({
getItems: function(listing) {
MailPoet.Ajax.post({
endpoint: 'subscribers',
action: 'listing',
data: {
offset: (listing.state.page - 1) * listing.state.limit,
limit: listing.state.limit,
group: listing.state.group,
search: listing.state.search,
sort_by: listing.state.sort_by,
sort_order: listing.state.sort_order
},
onSuccess: function(response) {
if(listing.isMounted()) {
listing.setState({
items: response.items || [],
filters: response.filters || [],
groups: response.groups || [],
count: response.count || 0,
loading: false
});
}
}.bind(listing)
});
},
renderItem: function(subscriber) { renderItem: function(subscriber) {
var rowClasses = classNames( var rowClasses = classNames(
'manage-column', 'manage-column',
@ -151,6 +123,7 @@ define(
render: function() { render: function() {
return ( return (
<Listing <Listing
endpoint="subscribers"
onRenderItem={ this.renderItem } onRenderItem={ this.renderItem }
items={ this.getItems } items={ this.getItems }
columns={ columns } columns={ columns }

View File

@ -11,7 +11,6 @@ define(
List, List,
Form Form
) { ) {
var DefaultRoute = Router.DefaultRoute; var DefaultRoute = Router.DefaultRoute;
var Link = Router.Link; var Link = Router.Link;
var Route = Router.Route; var Route = Router.Route;

View File

@ -35,4 +35,28 @@ class Newsletter extends Model {
static function group($orm, $group = null) { static function group($orm, $group = null) {
} }
public static function createOrUpdate($data = array()) {
$newsletter = false;
if(isset($data['id']) && (int)$data['id'] > 0) {
$newsletter = self::findOne((int)$data['id']);
}
if($newsletter === false) {
$newsletter = self::create();
$newsletter->hydrate($data);
} else {
unset($data['id']);
$newsletter->set($data);
}
$saved = $newsletter->save();
if($saved === false) {
return $newsletter->getValidationErrors();
} else {
return true;
}
}
} }

View File

@ -15,20 +15,6 @@ class Segment extends Model {
)); ));
} }
public static function createOrUpdate($model) {
$exists = self::where('name', $model['name'])
->find_one();
if($exists === false) {
$new_model = self::create();
$new_model->name = $model['name'];
return $new_model->save();
}
$exists->name = $model['name_updated'];
return $exists->save();
}
public function subscribers() { public function subscribers() {
return $this->has_many_through( return $this->has_many_through(
__NAMESPACE__.'\Subscriber', __NAMESPACE__.'\Subscriber',
@ -54,4 +40,28 @@ class Segment extends Model {
static function group($orm, $group = null) { static function group($orm, $group = null) {
} }
public static function createOrUpdate($data = array()) {
$segment = false;
if(isset($data['id']) && (int)$data['id'] > 0) {
$segment = self::findOne((int)$data['id']);
}
if($segment === false) {
$segment = self::create();
$segment->hydrate($data);
} else {
unset($data['id']);
$segment->set($data);
}
$saved = $segment->save();
if($saved === false) {
return $segment->getValidationErrors();
} else {
return true;
}
}
} }

View File

@ -80,10 +80,18 @@ class Subscriber extends Model {
if($subscriber === false) { if($subscriber === false) {
$subscriber = self::create(); $subscriber = self::create();
$subscriber->hydrate($data);
} else {
unset($data['id']);
$subscriber->set($data);
} }
$subscriber->hydrate($data); $saved = $subscriber->save();
return $subscriber->save();
if($saved === false) {
return $subscriber->getValidationErrors();
} else {
return true;
}
} }
} }

View File

@ -12,6 +12,17 @@ class Newsletters {
} }
function get($data = array()) { function get($data = array()) {
$id = (isset($data['id']) ? (int)$data['id'] : 0);
$newsletter = Newsletter::findOne($id);
if($newsletter === false) {
wp_send_json(false);
} else {
wp_send_json($newsletter->asArray());
}
}
function listing($data = array()) {
$listing = new Listing\Handler( $listing = new Listing\Handler(
\Model::factory('\MailPoet\Models\Newsletter'), \Model::factory('\MailPoet\Models\Newsletter'),
$data $data
@ -24,11 +35,14 @@ class Newsletters {
wp_send_json($collection); wp_send_json($collection);
} }
function save($args) { function save($data = array()) {
$model = Newsletter::create(); $result = Newsletter::createOrUpdate($data);
$model->hydrate($args);
$result = $model->save(); if($result !== true) {
wp_send_json($result); wp_send_json($result);
} else {
wp_send_json(true);
}
} }
function update($args) { function update($args) {

View File

@ -10,6 +10,17 @@ class Segments {
} }
function get($data = array()) { function get($data = array()) {
$id = (isset($data['id']) ? (int)$data['id'] : 0);
$segment = Segment::findOne($id);
if($segment === false) {
wp_send_json(false);
} else {
wp_send_json($segment->asArray());
}
}
function listing($data = array()) {
$listing = new Listing\Handler( $listing = new Listing\Handler(
\Model::factory('\MailPoet\Models\Segment'), \Model::factory('\MailPoet\Models\Segment'),
$data $data
@ -22,11 +33,14 @@ class Segments {
wp_send_json($collection); wp_send_json($collection);
} }
function save($args) { function save($data = array()) {
$model = Segment::create(); $result = Segment::createOrUpdate($data);
$model->hydrate($args);
$result = $model->save(); if($result !== true) {
wp_send_json($result); wp_send_json($result);
} else {
wp_send_json(true);
}
} }
function update($args) { function update($args) {

View File

@ -10,7 +10,7 @@ abstract class ValidModel extends \Model
'indexedErrors' => false, // If True getValidationErrors will return an array with the index 'indexedErrors' => false, // If True getValidationErrors will return an array with the index
// being the field name and the value the error. If multiple errors // being the field name and the value the error. If multiple errors
// are triggered for a field only the first will be kept. // are triggered for a field only the first will be kept.
'throw' => self::ON_SAVE // One of self::ON_SET|ON_SAVE|NEVER. 'throw' => self::ON_SAVE // One of self::ON_SET|ON_SAVE|NEVER.
// + ON_SET throws immediately when field is set() // + ON_SET throws immediately when field is set()
// + ON_SAVE throws on save() // + ON_SAVE throws on save()
// + NEVER means an exception is never thrown; check for ->getValidaionErrors() // + NEVER means an exception is never thrown; check for ->getValidaionErrors()
@ -130,11 +130,17 @@ abstract class ValidModel extends \Model
/** /**
* Overload set; to call validateAndSet * Overload set; to call validateAndSet
* // TODO: handle multiple sets if $name is a field=>val array
*/ */
public function set($name, $value = null) public function set($key, $value = null)
{ {
$this->validateAndSet($name, $value); if(is_array($key)) {
// multiple values
foreach($key as $field => $value) {
$this->validateAndSet($field, $value);
}
} else {
$this->validateAndSet($key, $value);
}
} }

View File

@ -23,10 +23,11 @@
"napa": "^1.2.0", "napa": "^1.2.0",
"papaparse": "4.1.1", "papaparse": "4.1.1",
"react": "^0.13.3", "react": "^0.13.3",
"react-checkbox-group": "^0.2.0",
"react-infinity": "^1.2.2", "react-infinity": "^1.2.2",
"react-prefixr": "^0.1.0", "react-prefixr": "^0.1.0",
"react-waypoint": "^1.0.2",
"react-router": "^0.13.3", "react-router": "^0.13.3",
"react-waypoint": "^1.0.2",
"select2": "3.5.1", "select2": "3.5.1",
"spectrum-colorpicker": "^1.6.2", "spectrum-colorpicker": "^1.6.2",
"tinymce": "4.1.10", "tinymce": "4.1.10",