diff --git a/docs/output-data-providers.md b/docs/output-data-providers.md index 372e355..a574f3d 100644 --- a/docs/output-data-providers.md +++ b/docs/output-data-providers.md @@ -1,20 +1,19 @@ -Data Providers -============== +# Data Providers In the [Pagination](/pwoli/api-docs/classes/Pagination.html) and [Sort](/pwoli/api-docs/classes/Sort.html) sections, we have described how to allow end users to choose a particular page of data to display and sort them by some columns. Because the task -of paginating and sorting data is very common, Pwoli provides a set of *data provider* classes to encapsulate it. +of paginating and sorting data is very common, Pwoli provides a set of _data provider_ classes to encapsulate it. A data provider is a class extending [DataProvider](/pwoli/api-docs/classes/DataProvider.html). It mainly supports retrieving paginated -and sorted data. It is usually used to work with [data widgets](/pwoli/output-data-widgets.md) so that end users can -interactively paginate and sort data. +and sorted data. It is usually used to work with [data widgets](/pwoli/output-data-widgets.md) so that end users can +interactively paginate and sort data. The following data provider classes are included in the Pwoli releases: -* [ActiveDataProvider](/pwoli/api-docs/classes/ActiveDataProvider.html): uses [query](/pwoli/api-docs/classes/ActiveDataProvider.html#query) to query data from databases - and return them in terms of arrays or [Active Record](/pwoli/api-docs/classes/Model.html) instances. -* [ArrayDataProvider](/pwoli/api-docs/classes/ArrayDataProvider.html): takes a big array and returns a slice of it based on the paginating and sorting - specifications. +- [ActiveDataProvider](/pwoli/api-docs/classes/ActiveDataProvider.html): uses [query](/pwoli/api-docs/classes/ActiveDataProvider.html#query) to query data from databases + and return them in terms of arrays or [Active Record](/pwoli/api-docs/classes/Model.html) instances. +- [ArrayDataProvider](/pwoli/api-docs/classes/ArrayDataProvider.html): takes a big array and returns a slice of it based on the paginating and sorting + specifications. The usage of all these data providers share the following common pattern: @@ -35,7 +34,7 @@ let count = provider.getCount(); let totalCount = provider.getTotalCount(); ``` -You specify the pagination and sorting behaviors of a data provider by configuring its +You specify the pagination and sorting behaviors of a data provider by configuring its [pagination](/pwoli/api-docs/classes/DataProvider.html#pagination) and [sort](/pwoli/api-docs/classes/DataProvider.html#sort) properties which correspond to the configurations for [Pagination](/pwoli/api-docs/classes/Pagination.html) and [Sort](/pwoli/api-docs/classes/Sort.html), respectively. You may also configure them to be `false` to disable pagination and/or sorting features. @@ -52,18 +51,18 @@ let grid = new GridView({ In the [Pagination](/pwoli/api-docs/classes/Pagination.html) and [Sort](/pwoli/api-docs/classes/Sort.html) sections, we have described how to allow end users to choose a particular page of data to display and sort them by some columns. Because the task -of paginating and sorting data is very common, Pwoli provides a set of *data provider* classes to encapsulate it. +of paginating and sorting data is very common, Pwoli provides a set of _data provider_ classes to encapsulate it. A data provider is a class implementing [DataProvider](/pwoli/api-docs/classes/DataProvider.html). It mainly supports retrieving paginated -and sorted data. It is usually used to work with [data widgets](/pwoli/output-data-widgets.md) so that end users can -interactively paginate and sort data. +and sorted data. It is usually used to work with [data widgets](/pwoli/output-data-widgets.md) so that end users can +interactively paginate and sort data. The following data provider classes are included in the Pwoli releases: -* [ActiveDataProvider](/pwoli/api-docs/classes/ActiveDataProvider.html): uses [query](/pwoli/api-docs/classes/ActiveDataProvider.html#query) to query data from databases - and return them in terms of arrays or [Active Record](/pwoli/api-docs/classes/Model.html) instances. -* [ArrayDataProvider](/pwoli/api-docs/classes/ArrayDataProvider.html): takes a big array and returns a slice of it based on the paginating and sorting - specifications. +- [ActiveDataProvider](/pwoli/api-docs/classes/ActiveDataProvider.html): uses [query](/pwoli/api-docs/classes/ActiveDataProvider.html#query) to query data from databases + and return them in terms of arrays or [Active Record](/pwoli/api-docs/classes/Model.html) instances. +- [ArrayDataProvider](/pwoli/api-docs/classes/ArrayDataProvider.html): takes a big array and returns a slice of it based on the paginating and sorting + specifications. The usage of all these data providers share the following common pattern: @@ -84,7 +83,7 @@ let count = provider.getCount(); let totalCount = provider.getTotalCount(); ``` -You specify the pagination and sorting behaviors of a data provider by configuring its +You specify the pagination and sorting behaviors of a data provider by configuring its [pagination](/pwoli/api-docs/classes/DataProvider.html#pagination) and [sort](/pwoli/api-docs/classes/DataProvider.html#sort) properties which correspond to the configurations for [Pagination](/pwoli/api-docs/classes/Pagination.html) and [Sort](/pwoli/api-docs/classes/Sort.html), respectively. You may also configure them to be `false` to disable pagination and/or sorting features. @@ -101,8 +100,7 @@ let grid = new GridView([ These data providers mainly vary in the way how the data source is specified. In the following subsections, we will explain the detailed usage of each of these data providers. - -## Active Data Provider +## Active Data Provider To use [ActiveDataProvider](/pwoli/api-docs/classes/ActiveDataProvider.html), you should configure its [query](/pwoli/api-docs/classes/ActiveDataProvider.html#query) property. It can take a [query](/pwoli/api-docs/classes/ActiveDataProvider.html#query) object. If the former, the data returned will be arrays; @@ -110,9 +108,9 @@ if the latter, the data returned can be either arrays or [Active Record](/pwoli/ For example, ```js -import { ActiveDataProvider} from 'pwoli'; +import { ActiveDataProvider } from 'pwoli'; -let query = where({status: 1}); +let query = where({ status: 1 }); let provider = new ActiveDataProvider({ query: {}, @@ -122,8 +120,8 @@ let provider = new ActiveDataProvider({ sort: { defaultOrder: { created_at: SORT_DESC, - title: SORT_ASC, - } + title: SORT_ASC, + }, }, }); @@ -136,17 +134,16 @@ If `query` in the above example is created using the following code, then the da ```js import Query; -let query = ({where: {status: 1}}); +let query = ({where: {status: 1}}); ``` > Note: If a query already specifies the `orderBy` clause, the new ordering instructions given by end users - (through the `sort` configuration) will be appended to the existing `orderBy` clause. Any existing `limit` - and `offset` clauses will be overwritten by the pagination request from end users (through the `pagination` configuration). +> (through the `sort` configuration) will be appended to the existing `orderBy` clause. Any existing `limit` +> and `offset` clauses will be overwritten by the pagination request from end users (through the `pagination` configuration). By default, [ActiveDataProvider](/pwoli/api-docs/classes/ActiveDataProvider.html) uses the `db` application component as the database connection. You may use a different database connection by configuring the [db](/pwoli/api-docs/classes/ActiveDataProvider.html#db) property. - ## Array Data Provider [ArrayDataProvider](/pwoli/api-docs/classes/ArrayDataProvider.html) is best used when working with a big array. The provider allows you to return @@ -178,22 +175,21 @@ let provider = new ArrayDataProvider({ // get the rows in the currently requested page let rows = provider.getModels(); -``` - -> Note: Compared to [Active Data Provider](#active-data-provider),array data provider is less efficient because it requires loading *all* data into the memory. +``` +> Note: Compared to [Active Data Provider](#active-data-provider),array data provider is less efficient because it requires loading _all_ data into the memory. ## Working with Data Keys When using the data items returned by a data provider, you often need to identify each data item with a unique key. For example, if the data items represent customer information, you may want to use the customer ID as the key -for each customer data. Data providers can return a list of such keys corresponding with the data items returned +for each customer data. Data providers can return a list of such keys corresponding with the data items returned by [DataProvider.getModels()](/pwoli/api-docs/classes/DataProvider.html#getModels). For example, ```js import { ActiveDataProvider } from 'pwoli'; -let query = {where: {status: 1}}; +let query = { where: { status: 1 } }; let provider = new ActiveDataProvider({ query: {}, @@ -223,23 +219,22 @@ provider = new ActiveDataProvider({ query: {}, key: function (model) { return md5(model.id); - } + }, }); ``` - ## Creating Custom Data Provider To create your own custom data provider classes, you should implement [DataProvider](/pwoli/api-docs/classes/DataProvider.html). An easier way is to extend from [DataProvider](/pwoli/api-docs/classes/DataProvider.html) which allows you to focus on the core data provider logic. In particular, you mainly need to implement the following methods: - -- [prepareModels](/pwoli/api-docs/classes/DataProvider.html#prepareModels): prepares the data models that will be made - available in the current page and returns them as an array. -- [prepareKeys](/pwoli/api-docs/classes/DataProvider.html#prepareKeys): accepts an array of currently available data models - and returns keys associated with them. -- [PrepareTotalCount](/pwoli/api-docs/classes/DataProvider.html#prepareTotalCount): returns a value indicating the total number - of data models in the data provider. + +- [prepareModels](/pwoli/api-docs/classes/DataProvider.html#prepareModels): prepares the data models that will be made + available in the current page and returns them as an array. +- [prepareKeys](/pwoli/api-docs/classes/DataProvider.html#prepareKeys): accepts an array of currently available data models + and returns keys associated with them. +- [PrepareTotalCount](/pwoli/api-docs/classes/DataProvider.html#prepareTotalCount): returns a value indicating the total number + of data models in the data provider. Below is an example of a data provider that reads CSV data efficiently: @@ -252,37 +247,37 @@ class CsvDataProvider extends DataProvider * @var string name of the CSV file to read */ public filename; - + /** * @var string|callable name of the key column or a callable returning it */ public key; - + /** * @var SplFileObject */ protected fileObject; // SplFileObject is very convenient for seeking to particular line in a file - - + + /** * {@inheritdoc} */ - public function init() + public init() { parent.init(); - + // open file this.fileObject = new SplFileObject(this.filename); } - + /** * {@inheritdoc} */ - protected function prepareModels() + protected prepareModels() { models = []; pagination = this.getPagination(); - + if (pagination === false) { // in case there's no pagination, read all lines while (!this.fileObject.eof()) { @@ -294,24 +289,24 @@ class CsvDataProvider extends DataProvider pagination.totalCount = this.getTotalCount(); this.fileObject.seek(pagination.getOffset()); limit = pagination.getLimit(); - + for (count = 0; count < limit; ++count) { models[] = this.fileObject.fgetcsv(); this.fileObject.next(); } } - + return models; } - + /** * {@inheritdoc} */ - protected function prepareKeys(models) + protected prepareKeys(models) { if (this.key !== null) { keys = []; - + foreach (models as model) { if (is_string(this.key)) { keys[] = model[this.key]; @@ -319,81 +314,31 @@ class CsvDataProvider extends DataProvider keys[] = call_user_func(this.key, model); } } - + return keys; } return array_keys(models); } - + /** * {@inheritdoc} */ - protected function prepareTotalCount() + protected prepareTotalCount() { count = 0; - + while (!this.fileObject.eof()) { this.fileObject.next(); ++count; } - + return count; } } ``` -## Filtering Data Providers using Data Filters +## Filtering Data Providers -While you can build conditions for active data provider manually as described in +You can easily build conditions for filtering data with active data provider manually as described in [Filtering Data](/pwoli/output-data-widgets.md#filtering-data) and [Separate Filter Form](/pwoli/output-data-widgets.md#separate-filter-form) -sections of data widgets guide, Pwoli has data filters that are very useful if you need flexible filter conditions. -Data filters could be used as follows: - -```js -let filter = new ActiveDataFilter({ - searchModel: 'app\models\PostSearch' -}); - -let filterCondition = null; - -// You may load filters from any source. For example, -// if you prefer JSON in request body, -// use Pwoli.app.request.getBodyParams() below: -if (filter.load(\Pwoli.app.request.get())) { - filterCondition = filter.build(); - if (filterCondition === false) { - // Serializer would get errors out of it - return filter; - } -} - -let query = {}; -if (filterCondition !== null) { - query.andWhere(filterCondition); -} - -return new ActiveDataProvider({ - query: {}, -}); -``` - -`PostSearch` model serves the purpose of defining which properties and values are allowed for filtering: - -```js -import { Model } from 'pwoli'; - -class PostSearch extends Model -{ - public id; - public title; - - public function rules() - { - return [ - ['id', 'integer'], - ['title', 'string', {min: 2}, {max: 200}], - ]; - } -} -``` \ No newline at end of file diff --git a/src/helpers/DataHelper.ts b/src/helpers/DataHelper.ts index b1b80d1..cfa9ffe 100644 --- a/src/helpers/DataHelper.ts +++ b/src/helpers/DataHelper.ts @@ -522,4 +522,50 @@ export default class DataHelper extends Component { return defaultValue; } + + public static substrCompare(mainStr, str, offset, length, caseInsensitivity = false) { + // eslint-disable-line camelcase + // discuss at: https://locutus.io/php/substr_compare/ + // original by: Brett Zamir (https://brett-zamir.me) + // original by: strcasecmp, strcmp + // example 1: substr_compare("abcde", "bc", 1, 2) + // returns 1: 0 + if (!offset && offset !== 0) { + throw new Error('Missing offset for substr_compare()'); + } + if (offset < 0) { + offset = mainStr.length + offset; + } + if (length && length > mainStr.length - offset) { + return false; + } + length = length || mainStr.length - offset; + mainStr = mainStr.substr(offset, length); + // Should only compare up to the desired length + str = str.substr(0, length); + if (caseInsensitivity) { + // Works as strcasecmp + mainStr = (mainStr + '').toLowerCase(); + str = (str + '').toLowerCase(); + if (mainStr === str) { + return 0; + } + return mainStr > str ? 1 : -1; + } + // Works as strcmp + return mainStr === str ? 0 : mainStr > str ? 1 : -1; + } + + public static strcmp(str1, str2) { + // http://kevin.vanzonneveld.net + // + original by: Waldo Malqui Silva + // + input by: Steve Hilder + // + improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net) + // + revised by: gorthaur + // * example 1: strcmp( 'waldo', 'owald' ); + // * returns 1: 1 + // * example 2: strcmp( 'owald', 'waldo' ); + // * returns 2: -1 + return str1 == str2 ? 0 : str1 > str2 ? 1 : -1; + } } diff --git a/src/helpers/Html.ts b/src/helpers/Html.ts index 203317d..fefde94 100644 --- a/src/helpers/Html.ts +++ b/src/helpers/Html.ts @@ -2,11 +2,16 @@ import Model from '../base/Model'; import Pwoli from '../base/Application'; import Component from '../base/Component'; import DataHelper from './DataHelper'; -import DataProvider from '../data//DataProvider'; /** - * BaseHtml provides concrete implementation for [[Html]]. + * Html provides a set of static methods for generating commonly used HTML tags. * - * Do not use BaseHtml. Use [[Html]] instead. + * Nearly all of the methods in this class allow setting additional html attributes for the html + * tags they generate. You can specify, for example, `class`, `style` or `id` for an html element + * using the `$options` parameter. See the documentation of the [[tag]] method for more details. + * + * For more details and usage information on Html, see the [guide article on html helpers](guide:helper-html). + * + * @author Mahesh S Warrier */ export default class Html extends Component { /** @@ -1043,7 +1048,7 @@ export default class Html extends Component { * @return the generated drop-down list tag */ public static activeDropDownList(model, attribute, items, options: any = {}) { - if (options.multiple.length === 0) { + if (options.multiple !== true) { return this.activeListInput('dropDownList', model, attribute, items, options); } @@ -1205,7 +1210,222 @@ export default class Html extends Component { if (options.id === undefined) { options.id = this.getInputId(model, attribute); } - return this[type](name, selection, items, options); } + /** + * Generates a drop-down list. + * @param name the input name + * @param selection the selected value(s). String for single or array for multiple selection(s). + * @param items the option data items. The array keys are option values, and the array values + * are the corresponding option labels. The array can also be nested (i.e. some array values are arrays too). + * For each sub-array, an option group will be generated whose label is the key associated with the sub-array. + * If you have a list of data models, you may convert them into the format described above using + * `Array.map()`. + * Note, the values and labels will be automatically HTML-encoded by this method, and the blank spaces in + * the labels will also be HTML-encoded. + * @param options the tag options in terms of name-value pairs. The following options are specially handled: + * + * - prompt: string, a prompt text to be displayed as the first option. Since version 2.0.11 you can use an array + * to override the value and to set other tag attributes: + * + * ```js + * { text: 'Please select', options: { value: 'none', class: 'prompt', label: 'Select' } }, + * ``` + * + * - options: array, the attributes for the select option tags. The array keys must be valid option values, + * and the array values are the extra attributes for the corresponding option tags. For example, + * + * ```js + * { + * value1: { disabled: true }, + * value2: { label: 'value 2' }, + * } + * ``` + * + * - groups: array, the attributes for the optgroup tags. The structure of this is similar to that of 'options', + * except that the array keys represent the optgroup labels specified in $items. + * - encodeSpaces: bool, whether to encode spaces in option prompt and option value with ` ` character. + * Defaults to false. + * - encode: bool, whether to encode option prompt and option value characters. + * Defaults to `true`. This option is available since 2.0.3. + * - strict: boolean, if `$selection` is an array and this value is true a strict comparison will be performed on `$items` keys. Defaults to false. + * + * The rest of the options will be rendered as the attributes of the resulting tag. The values will + * be HTML-encoded using [[encode]]. If a value is null, the corresponding attribute will not be rendered. + * See [[renderTagAttributes]] for details on how attributes are being rendered. + * + * @return The generated drop-down list tag + */ + public static dropDownList(name, selection = null, items = [], options: { [key: string]: any } = {}) { + if (options.multiple !== undefined) { + return this.listBox(name, selection, items, options); + } + options['name'] = name; + delete options['unselect']; + let selectOptions = this.renderSelectOptions(selection, items, options); + return this.tag('select', '\n' + selectOptions + '\n', options); + } + /** + * Generates a list box. + * @param name the input name + * @param selection the selected value(s). String for single or array for multiple selection(s). + * @param items the option data items. The array keys are option values, and the array values + * are the corresponding option labels. The array can also be nested (i.e. some array values are arrays too). + * For each sub-array, an option group will be generated whose label is the key associated with the sub-array. + * If you have a list of data models, you may convert them into the format described above using + * `Array.map()`. + * + * Note, the values and labels will be automatically HTML-encoded by this method, and the blank spaces in + * the labels will also be HTML-encoded. + * @param options the tag options in terms of name-value pairs. The following options are specially handled: + * + * - prompt: string, a prompt text to be displayed as the first option. Since version 2.0.11 you can use an array + * to override the value and to set other tag attributes: + * + * ```js + * { text: 'Please select', options: { value: 'none', class: 'prompt', label: 'Select' } }, + * ``` + * + * - options: array, the attributes for the select option tags. The array keys must be valid option values, + * and the array values are the extra attributes for the corresponding option tags. For example, + * ```js + * { + * value1: { disabled: true }, + * value2: { label: 'value 2' }, + * } + * ``` + * ``` + * + * - groups: array, the attributes for the optgroup tags. The structure of this is similar to that of 'options', + * except that the array keys represent the optgroup labels specified in $items. + * - unselect: string, the value that will be submitted when no option is selected. + * When this attribute is set, a hidden field will be generated so that if no option is selected in multiple + * mode, we can still obtain the posted unselect value. + * - encodeSpaces: bool, whether to encode spaces in option prompt and option value with ` ` character. + * Defaults to false. + * - encode: bool, whether to encode option prompt and option value characters. + * Defaults to `true`. + * - strict: boolean, if `$selection` is an array and this value is true a strict comparison will be performed on `$items` keys. Defaults to false. + * + * The rest of the options will be rendered as the attributes of the resulting tag. The values will + * be HTML-encoded using [[encode]]. If a value is null, the corresponding attribute will not be rendered. + * See [[renderTagAttributes]] for details on how attributes are being rendered. + * + * @return The generated list box tag + */ + public static listBox(name: string, selection = null, items = [], options: { [key: string]: any } = {}) { + if (options.size === undefined) { + options['size'] = 4; + } + if (options.multiple !== undefined && name.length > 0 && DataHelper.substrCompare(name, '[]', -2, 2)) { + name += '[]'; + } + options['name'] = name; + let hidden; + if (options['unselect'] !== undefined) { + // add a hidden field so that if the list box has no option being selected, it still submits a value + if (name.length > 0 && DataHelper.substrCompare(name, '[]', -2, 2) === 0) { + name = name.substr(0, -2); + } + let hiddenOptions = []; + // make sure disabled input is not sending any value + if (options['disabled'] !== undefined) { + hiddenOptions['disabled'] = options['disabled']; + } + hidden = this.hiddenInput(name, options['unselect'], hiddenOptions); + delete options['unselect']; + } else { + hidden = ''; + } + let selectOptions = this.renderSelectOptions(selection, items, options); + return hidden + this.tag('select', '\n' + selectOptions + '\n', options); + } + /** + * Renders the option tags that can be used by [[dropDownList]] and [[listBox]]. + * @param selection the selected value(s). String for single or array for multiple selection(s). + * @param items the option data items. The array keys are option values, and the array values + * are the corresponding option labels. The array can also be nested (i.e. some array values are arrays too). + * For each sub-array, an option group will be generated whose label is the key associated with the sub-array. + * If you have a list of data models, you may convert them into the format described above using + * `Array.map()` + * + * Note, the values and labels will be automatically HTML-encoded by this method, and the blank spaces in + * the labels will also be HTML-encoded. + * @param tagOptions the $options parameter that is passed to the [[dropDownList]] or [[listBox]] call. + * This method will take out these elements, if any: "prompt", "options" and "groups". See more details + * in [[dropDownList]] for the explanation of these elements. + * + * @return String the generated list options + */ + public static renderSelectOptions(selection, items, tagOptions: { [key: string]: any } = {}) { + var lines, + encodeSpaces, + encode, + strict, + promptOptions, + promptText, + options, + groups, + key, + value, + groupAttrs, + attrs, + content, + text; + lines = []; + encodeSpaces = DataHelper.remove(tagOptions, 'encodeSpaces', false); + encode = DataHelper.remove(tagOptions, 'encode', true); + strict = DataHelper.remove(tagOptions, 'strict', false); + if (tagOptions.prompt !== undefined) { + promptOptions = { value: '' }; + if (typeof tagOptions['prompt'] === 'string') { + promptText = tagOptions['prompt']; + } else { + promptText = tagOptions['prompt']['text']; + promptOptions = { ...promptOptions, ...tagOptions['prompt']['options'] }; + } + promptText = encode ? this.encode(promptText) : promptText; + if (encodeSpaces) { + promptText = promptText.replace(' ', ' '); + } + lines.push(this.tag('option', promptText, promptOptions)); + } + + options = tagOptions.options !== undefined ? tagOptions['options'] : {}; + groups = tagOptions.groups !== undefined ? tagOptions['groups'] : {}; + delete tagOptions.prompt, tagOptions.options, tagOptions.groups; + options.encodeSpaces = DataHelper.getValue(options, 'encodeSpaces', encodeSpaces); + options.encode = DataHelper.getValue(options, 'encode', encode); + + for (key in items) { + value = items[key]; + if (typeof value === 'object') { + groupAttrs = groups[key] !== undefined ? groups[key] : {}; + if (groupAttrs['label'] === undefined) groupAttrs['label'] = key; + attrs = { + options: options, + groups: groups, + encodeSpaces: encodeSpaces, + encode: encode, + strict: strict, + }; + content = this.renderSelectOptions(selection, value, attrs); + lines.push(this.tag('optgroup', '\n' + content + '\n', groupAttrs)); + } else { + attrs = options[key] !== undefined ? options[key] : {}; + attrs['value'] = key as string; + console.log('attrs.selected', options); + if (attrs.selected === undefined && selection !== undefined && !DataHelper.strcmp(key, selection)) { + attrs.selected = true; + } + text = encode ? this.encode(value) : value; + if (encodeSpaces) { + text = text.replace(' ', ' '); + } + lines.push(this.tag('option', text, attrs)); + } + } + + return lines.join('\n'); + } } diff --git a/src/orm-adapters/IORMAdapter.ts b/src/orm-adapters/IORMAdapter.ts index 28766e4..a836171 100644 --- a/src/orm-adapters/IORMAdapter.ts +++ b/src/orm-adapters/IORMAdapter.ts @@ -1,10 +1,23 @@ import { ActiveDataProvider, Pagination, Sort } from '..'; import Component from '../base/Component'; import Model from '../base/Model'; - +/** + * Every ORM Adapter created for Pwoli must implement this interface as these are the set of properties and methods + * required for Pwoli's ORM related operations. + * + * @author Mahesh S Warrier + */ export default interface IORMAdapter extends Component { + /** + * The base Model class of the ORM. + */ modelClass: Model; + /** + * This is the object which is used to map the validators in the ORM with the client side validators in Pwoli. + * Keys of this object are the validator string for the ORM and the values are the corresponding validators in Pwoli. + */ validatorMap: { [key: string]: string }; + findAll(query: { [key: string]: any }): Promise; applySort(query: { [key: string]: any }, sort: Sort): { [key: string]: any }; applyPagination(query: { [key: string]: any }, pagination): { [key: string]: any }; diff --git a/src/orm-adapters/ORMAdapter.ts b/src/orm-adapters/ORMAdapter.ts index db4cb0e..1ad78f6 100644 --- a/src/orm-adapters/ORMAdapter.ts +++ b/src/orm-adapters/ORMAdapter.ts @@ -1,7 +1,9 @@ -import Pwoli from '../base/Pwoli'; import Application from '../base/Application'; import Component from '../base/Component'; -import IORMAdapter from './IORMAdapter'; +/** + * ORMAdapter should be the base class for all ORM Adapters(like [[SequelizeAdapter]]) in Pwoli. + * It serves as the interface between Pwoli and the ORM being used. + */ export default class ORMAdapter extends Component { public modelClass; diff --git a/src/orm-adapters/SequelizeAdapter.ts b/src/orm-adapters/SequelizeAdapter.ts index 517a1c9..642ae52 100644 --- a/src/orm-adapters/SequelizeAdapter.ts +++ b/src/orm-adapters/SequelizeAdapter.ts @@ -3,8 +3,13 @@ import Model from '../base/Model'; import Sort from '../data/Sort'; import IORMAdapter from './IORMAdapter'; import ORMAdapter from './ORMAdapter'; - +/** + * SequelizeAdapter is the ORM connectivity interface between Pwoli and the Sequelize ORM. + * @see [Using a different ORM](/pwoli/using-another-orm) + * @author Mahesh S Warrier + */ export default class SequelizeAdapter extends ORMAdapter implements IORMAdapter { + /** @inheritdoc */ public validatorMap: { [key: string]: string } = { is: 'regex', not: 'regexInverse', @@ -17,7 +22,6 @@ export default class SequelizeAdapter extends ORMAdapter implements IORMAdapter Object.assign(this, config); } - /** seeds property comment */ public async findAll(query: { [key: string]: any }): Promise { return await this.modelClass.findAll(query); } @@ -49,10 +53,8 @@ export default class SequelizeAdapter extends ORMAdapter implements IORMAdapter public setAttributes(model: Model, values: { [key: string]: any }): Model { for (let attribute in values) { if (values[attribute] !== undefined) { - if(model.rawAttributes[attribute] !== undefined) - model[attribute] = values[attribute]; - else - model.dataValues[attribute] = values[attribute]; + if (model.rawAttributes[attribute] !== undefined) model[attribute] = values[attribute]; + else model.dataValues[attribute] = values[attribute]; } } return model;