MobX decorating a class causes function to throw TypeError - mobx

I have two data classes (business logic that I didn't write), that I'm trying to use in a store. I have fixed issues in that store by decorating them. They are defined to be:
// data classes; // these would be in separate file.
class PayRate {
/**
* #param {number} storeId The store id of the pay rate. (-1 for all)
* #param {number} empId The id of the employee. (-1 for all)
* #param {number} taskId The id of the task. (-1 for all)
* #param {Date} effectiveDate The date the payrate goes in effect.
* #param {number} rate The rate of pay.
*/
constructor(storeId, empId, taskId, effectiveDate, rate) {
this.StoreId = storeId || -1;
this.EmpId = empId || -1;
this.TaskId = taskId || -1;
this.EffectiveDate = effectiveDate ? new Date(effectiveDate) : new Date();
this.Rate = rate || 0.00;
this.OriginalObject = $.extend(true, {}, this);
}
/**
* Gets a readable version of the effective date.
* #returns {string} A -m/dd/yyyy representation of the effective date.
*/
GetReadableDate() {
return this.EffectiveDate.toLocaleDateString("en-US");
}
/**
* Gets a one line description of the pay rate.
* #returns {string} A string in the form of (date) - payrate.
*/
GetDescription() {
return `(${this.GetReadableDate()}) - $${this.Rate.toFixed(2)}`;
}
/**
* Gets a one line description of the pay rate.
* Does the exact same as GetDescription(), but is overload of Object.prototype.toString, which allows for stringification of Objects
* #returns {string} A string in the form of (date) - payrate.
*/
toString() {
return `(${this.GetReadableDate()}) - $${this.Rate.toFixed(2)}`;
}
/**
* Tells whether a pay rate was changed or not.
* #returns {boolean} A boolean saying whether or not the pay rate was changed.
*/
IsChanged() {
if (this.EffectiveDate.getTime() !== this.OriginalObject.EffectiveDate.getTime()) {
return true;
}
if (this.Rate != this.OriginalObject.Rate) {
return true;
}
if (this._deleted) {
return true;
}
return false;
}
/**
* Reverts the changes back to the original.
*/
RevertChanges() {
$.extend(true, this, this.OriginalObject);
}
}
mobx.decorate(PayRate, {
StoreId : mobx.observable,
EmpId : mobx.observable,
TaskId : mobx.observable,
EffectiveDate : mobx.observable,
Rate : mobx.observable,
})
class TaskObject {
/**
* #param {Task} task The task that is being kept track of.
* #param {PermissionLink[]} permissionLinks A list of permission links that are being kept track of.
* #param {PayRate[]} payrates A list of pay rates that are being kept track of.
*/
constructor(task, permissionLinks = [], payrates = []) {
this.Model = $.extend(true, {}, task);
this.Model.Type = 25;
this.PermissionLinks = $.extend(true, [], permissionLinks);
this.Payrates = $.extend(true, [], payrates);
this.OriginalObject = $.extend(true, {}, this);
}
/**
* Gives the dirty status of the task object.
* #returns {boolean} Tells whether or not the TaskObject has been changed.
*/
IsChanged() {
if (this.Model.Id == -1) {
return true;
}
if (this.Model.Name != this.OriginalObject.Model.Name) {
return true;
}
if (this.Model.GroupName != this.OriginalObject.Model.GroupName) {
return true;
}
if (this.Model.DescStr != this.OriginalObject.Model.DescStr) {
return true;
}
if (this.Model.IsActive != this.OriginalObject.Model.IsActive) {
return true;
}
if (this.PermissionLinks.length != this.OriginalObject.PermissionLinks.length) {
return true;
}
for (let i = 0; i < this.PermissionLinks.length; i++) {
const element = this.PermissionLinks[i];
const compElement = this.OriginalObject.PermissionLinks[i];
if (JSON.stringify(element) !== JSON.stringify(compElement)) {
return true;
}
}
for (let i = 0; i < this.Payrates.length; i++) {
const payrate = this.Payrates[i];
if (payrate.IsChanged()) {
return true;
}
}
return false
}
/**
* Reverts the changes that are on the task object.
* #returns {TaskObject} The TaskObject with its changes discarded.
*/
RevertChanges() {
this.Model = $.extend(true, {}, this.OriginalObject.Model);
this.PermissionLinks = $.extend(true, [], this.OriginalObject.PermissionLinks);
for (let i = 0; i < this.Payrates.length; i++) {
this.Payrates[i].RevertChanges()
}
return this;
}
/**
* This is here for debugging purposes (i.e. with data stores that use it) and may be overwritten with business logic at any time
*/
toString() {
return JSON.stringify(this, null, '\t')
}
}
// marking all the properties of TaskObject observable
mobx.decorate(TaskObject, {
Model : mobx.observable,
PermissionLinks : mobx.observable,
Payrates : mobx.observable,
RevertChanges : mobx.action,
IsChanged : mobx.computed
})
Now, doing new PayRate().IsChanged() works as expected, but new TaskObject.IsChanged() results in:
Uncaught TypeError: Cannot read property 'call' of undefined
at e.computeValue (mobx.umd.min.js:1)
at e.get (mobx.umd.min.js:1)
at e.read (mobx.umd.min.js:1)
at TaskObject.get (mobx.umd.min.js:1)
at pen.js:163
e.computeValue # mobx.umd.min.js:1
e.get # mobx.umd.min.js:1
e.read # mobx.umd.min.js:1
get # mobx.umd.min.js:1
(anonymous) # pen.js:163
Here is demo
Why is this happening and how can I fix it?

You declare IsChanged as a function but decorate it as a computed property, then call it as a function. If you want it to be a computed property, you'd need to:
convert it to a getter: IsChanged() -> get IsChanged()
call it without parenthesis: TaskObject.IsChanged

Related

How to extend this function. The task is getLinkedPages must be created and return content depending on the parameter

How to extend this function. The task is getLinkedPages must be created and return content depending on the parameter.
/**
* Get the contents as a sorted collection tree.
*/
public function getTree(bool $showDrafts = false, bool $renderPages = false, bool $renderLinkedPages = false): Collection
{
$pages = $this->getPages($showDrafts, $renderPages);
$chapters = Chapter::visible()->where('book_id', '=', $this->book->id)->get();
$all = collect()->concat($pages)->concat($chapters);
$chapterMap = $chapters->keyBy('id');
$lonePages = collect();
$pages->groupBy('chapter_id')->each(function ($pages, $chapter_id) use ($chapterMap, &$lonePages) {
$chapter = $chapterMap->get($chapter_id);
if ($chapter) {
$chapter->setAttribute('visible_pages', collect($pages)->sortBy($this->bookChildSortFunc()));[enter image description here][1]
} else {
$lonePages = $lonePages->concat($pages);
}
});
$chapters->whereNull('visible_pages')->each(function (Chapter $chapter) {
$chapter->setAttribute('visible_pages', collect([]));
});
$all->each(function (Entity $entity) use ($renderPages) {
$entity->setRelation('book', $this->book);
if ($renderPages && $entity instanceof Page) {
$entity->html = (new PageContent($entity))->render();
}
});
return collect($chapters)->concat($lonePages)->sortBy($this->bookChildSortFunc());
}

Prestashop 1.7 - City field as dropdown or autocomplete

I am using Prestashop 1.7 and I need to change the city input field.
I have a billing software that only allows me to use predefined cities. So I have created a table ps_cities with the entries (id an city name).
I know how to write a dropdown or a autocomplete script, but I do not know where to change the input type in the Prestashop files.
On the 1.6 version you have the input field in a theme file, but somehow I fail to find in the new version.
In PrestaShop 1.7.7.X I've created a module that includes some new (and cool!) hooks like showing below. I consider this one a good option because it will be easier to maintain in the next PrestaShop releases.
Some assumptions here: I created a relationship model CityAddress with two fields id_city and id_address and a City model with fields like name, id_state, id_country, also I continued using Address::city string name for compatibility.
/**
* #see /classes/form/CustomerAddressFormatter.php#L156
* #param array $param [
* #var array $fields
* ]
*/
public function hookAdditionalCustomerAddressFields($params)
{
($params['fields']['city'])->setType('hidden');
// New field
$formField = $params['fields'];
$formField = (new FormField())
->setName('id_city')
->setLabel($this->l('City'))
->setRequired(true)
->setType('select')
;
// If an address already exits, select the default city
if (Tools::getIsset('id_address')) {
$address = new Address(Tools::getValue('id_address'));
if (!empty($address->id_state)) {
$cities = City::getCitiesByIdState((int) $address->id_state);
if (!empty($cities)) {
foreach ($cities as $city) {
$formField->addAvailableValue(
$city['id_city'],
$city['name']
);
}
$id_city = CityAddress::getIdCityByIdAddress((int) $address->id);
$formField->setValue($id_city);
}
}
}
// Add the id_city field in the position of the city field
$keys = array_keys($params['fields']);
$search = 'city';
foreach ($keys as $key => $value) {
if ($value == $search) {
break;
}
}
$part1 = array_slice($params['fields'], 0, $key + 1);
$part2 = array_slice($params['fields'], $key + 1);
$part1['id_city'] = $formField;
$params['fields'] = array_merge($part1, $part2);
}
This one to validate the field:
/**
* #see /classes/form/CustomerAddressForm.php#L123
* #param array $param [
* #var CustomerAddressForm $form
* ]
*/
public function hookActionValidateCustomerAddressForm($params)
{
if (empty(Tools::getValue('id_city'))
|| empty(Tools::getValue('city'))) {
return false;
}
$form = $params['form'];
$idCityField = $form->getField('id_city');
$idCity = (int) Tools::getValue('id_city');
$cityObj = new City($idCity);
$city = pSQL(Tools::getValue('city'));
if ($cityObj->name !== $city) {
$idCityField->addError(sprintf(
$this->l('Invalid name in field id_city %s and city %s'),
$cityObj->name,
$city
));
return false;
}
return true;
}
And the submitted field:
/**
* #see /classes/form/CustomerAddressForm.php#L153
* #param array $param [
* #var Address $address
* ]
*/
public function hookActionSubmitCustomerAddressForm($params)
{
/** #var Address */
$address = $params['address'];
$address->save();
if (!Validate::isLoadedObject($address)) {
throw new PrestaShopException($this->l('Address object error while trying to save city'));
}
// If address has a previous value then update it
$cityAddress = CityAddress::getCityAddressByIdAddress((int) $address->id);
$city = City::getCityByNameAndIdState($address->city, $address->id_state);
$cityAddress->id_city = $city->id;
$cityAddress->id_address = $address->id;
$cityAddress->save();
}
It is possible if you have this line in the additionalCustomerAddressFields hook:
https://github.com/PrestaShop/PrestaShop/blob/develop/classes/form/CustomerAddressFormatter.php#L150
For previous version I included ['fields' => &$format] as a parameter.
You can find all form fields of the Front Office in your theme's /templates/_partials/form-fields.tpl file

Is there a way to optimize the mapping of users?

I've created a method to call this getUserIdsByAllBranches() and the return would be an array. If array is not empty, I'll be doing another process. But I have a problem here in mapping or getting all the users with all branches.
Here's the code:
/**
* Get all User Ids of Branch Permission User
* By All Branches
* #param Array
* #return Array
*/
public function getUserIdsByAllBranches($override = [])
{
if (count($override) !== 0) {
foreach($override as $property => $value) {
${$property} = $value;
}
} else {
$branchLength = App::make(BranchRepository::class)->get()->count();
}
$userIdsWithAllBranches = [];
$userIds = $this->getUserIds();
foreach($userIds as $userId) {
$identical = true;
$userBranchIds = array_values($this->get()
->where('user_id', $userId)
->pluck(['branch_id'])
->unique()
->toArray());
if(count($userBranchIds) === $branchLength) {
$initialPermissions = [];
foreach($userBranchIds as $index => $userBranchId) {
if(!$identical) {
continue;
}
$branchUserPermissions = $this->get()
->where('user_id', $userId)
->where('branch_id', $userBranchId)
->sortBy('permission_id')
->pluck(['permission_id']);
if($index === 0) {
$initialPermissions = $branchUserPermissions;
} else {
if($initialPermissions != $branchUserPermissions) {
$identical = false;
}
}
}
} else {
$identical = false;
}
if($identical) {
array_push($userIdsWithAllBranches, $userId);
}
}
return $userIdsWithAllBranches;
}
/**
* Get all User Ids of Branch Permission User
*
* #return Array
*/
public function getUserIds()
{
return array_values($this->get()
->sortBy('user_id')
->pluck(['user_id'])
->unique()
->toArray());
}
This method getUserIds(), will be returning all the user ids for branch permission user table.
Branch Permission User Table:
branch_id
permission_id
user_id

Doesn't save the image in the database Symfony 4

I'm writing applications in symfony and I would like to add an image to the article. I can't save the image to the database. I am trying to make an example from the documentation: https://symfony.com/doc/current/controller/upload_file.html
The image is uploaded to the directory on the server and isn't saved to the database.
$post->setThumbnail($fileName); sets the image name correctly
Entity
/**
* #Assert\Image(
* minWidth = 500,
* minHeight = 300,
* maxWidth = 1920,
* maxHeight = 1080,
* maxSize = "1M"
* )
*/
private $thumbnail;
/**
* Set thumbnail.
*
* #param string $thumbnail
*
* #return Post
*/
public function setThumbnail($thumbnail)
{
$this->thumbnail = $thumbnail;
return $this;
}
/**
* Get thumbnail.
*
* #return string
*/
public function getThumbnail()
{
return $this->thumbnail;
}
Action in Controller
/**
* Add and Edit page Post.
*
* #Route(
* {"pl": "/artykyl/{slug}"},
* name="panel_post",
* defaults={"slug"=NULL}
* )
*
* #param Request $request
* #param string|null $slug
* #param TranslatorInterface $translator
*
* #return \Symfony\Component\HttpFoundation\RedirectResponse|\Symfony\Component\HttpFoundation\Response
*/
public function post(Request $request, string $slug = null, TranslatorInterface $translator, FileUploader $fileUploader)
{
if (null === $slug) {
$post = new Post();
$newPostForm = true;
} else {
$post = $this->getDoctrine()->getRepository('App\Entity\Post')->findOneBy(['slug' => $slug]);
}
$form = $this->createForm(PostType::class, $post);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
if ($post->getThumbnail()) {
$file = $post->getThumbnail();
$fileName = $fileUploader->upload($file);
$post->setThumbnail($fileName);
}
$em = $this->getDoctrine()->getManager();
$em->persist($post);
$em->flush();
$this->addFlash('success', $translator->trans('Changes have been saved'));
return $this->redirectToRoute('panel_post', ['slug' => $post->getSlug()]);
} elseif ($form->isSubmitted() && false === $form->isValid()) {
$this->addFlash('danger', $translator->trans('Corrects form'));
}
return $this->render('backend/blog/post.html.twig', [
'currPage' => 'posts',
'form' => $form->createView(),
'post' => $post,
]);
}
File Uploader
class FileUploader
{
private $targetDirectory;
public function __construct($targetDirectory)
{
$this->targetDirectory = $targetDirectory;
}
public function upload(UploadedFile $file)
{
$fileName = md5(uniqid()).'.'.$file->guessExtension();
try {
$file->move($this->getTargetDirectory(), $fileName);
} catch (FileException $e) {
// ... handle exception if something happens during file upload
}
return $fileName;
}
public function getTargetDirectory()
{
return $this->targetDirectory;
}
}
Listener ThumbnailUploadListener
class ThumbnailUploadListener
{
private $uploader;
public function __construct(FileUploader $uploader)
{
$this->uploader = $uploader;
}
public function prePersist(LifecycleEventArgs $args)
{
$entity = $args->getEntity();
$this->uploadFile($entity);
}
public function preUpdate(PreUpdateEventArgs $args)
{
$entity = $args->getEntity();
$this->uploadFile($entity);
}
private function uploadFile($entity)
{
// upload only works for Post entities
if (!$entity instanceof Post) {
return;
}
$file = $entity->getThumbnail();
// only upload new files
if ($file instanceof UploadedFile) {
$fileName = $this->uploader->upload($file);
$entity->setThumbnail($fileName);
} elseif ($file instanceof File) {
// prevents the full file path being saved on updates
// as the path is set on the postLoad listener
$entity->setThumbnail($file->getFilename());
}
}
public function postLoad(LifecycleEventArgs $args)
{
$entity = $args->getEntity();
if (!$entity instanceof Post) {
return;
}
if ($fileName = $entity->getThumbnail()) {
$entity->setPost(new File($this->uploader->getTargetDirectory().'/'.$fileName));
}
}
}
services.yaml
App\EventListener\ThumbnailUploadListener:
tags:
- { name: doctrine.event_listener, event: prePersist }
- { name: doctrine.event_listener, event: preUpdate }
- { name: doctrine.event_listener, event: postLoad }
You forgot about mapping the thumbnail field
#ORM\Column(type="string")

SQL "VIEW" in codeigniter

I want to create an SQL view within the codeigniter model. What is the best way of doing this?
I use mutliple models depending on wich table i need to work
application/core/MY_model.php
<?php
/**
* CRUD Model
* A base model providing CRUD, pagination and validation.
*/
class MY_Model extends CI_Model
{
public $table;
public $primary_key;
public $default_limit = 15;
public $page_links;
public $query;
public $form_values = array();
protected $default_validation_rules = 'validation_rules';
protected $validation_rules;
public $validation_errors;
public $total_rows;
public $date_created_field;
public $date_modified_field;
public $native_methods = array(
'select', 'select_max', 'select_min', 'select_avg', 'select_sum', 'join',
'where', 'or_where', 'where_in', 'or_where_in', 'where_not_in', 'or_where_not_in',
'like', 'or_like', 'not_like', 'or_not_like', 'group_by', 'distinct', 'having',
'or_having', 'order_by', 'limit'
);
function __construct()
{
parent::__construct();
}
public function __call($name, $arguments)
{
call_user_func_array(array($this->db, $name), $arguments);
return $this;
}
/**
* Sets CI query object and automatically creates active record query
* based on methods in child model.
* $this->model_name->get()
*/
public function get($with = array(), $include_defaults = true)
{
if ($include_defaults) {
$this->set_defaults();
}
foreach ($with as $method) {
$this->$method();
}
$this->query = $this->db->get($this->table);
return $this;
}
/**
* Query builder which listens to methods in child model.
* #param type $exclude
*/
private function set_defaults($exclude = array())
{
$native_methods = $this->native_methods;
foreach ($exclude as $unset_method) {
unset($native_methods[array_search($unset_method, $native_methods)]);
}
foreach ($native_methods as $native_method) {
$native_method = 'default_' . $native_method;
if (method_exists($this, $native_method)) {
$this->$native_method();
}
}
}
/**
* Call when paginating results.
* $this->model_name->paginate()
*/
public function paginate($with = array())
{
$uri_segment = '';
$offset = 0;
$per_page = $this->default_limit;
$this->load->helper('url');
$this->load->library('pagination');
$this->set_defaults();
foreach ($with as $method) {
$this->$method();
}
$this->total_rows = $this->db->count_all_results($this->table);
$uri_segments = $this->uri->segment_array();
foreach ($uri_segments as $key => $segment) {
if ($segment == 'page') {
$uri_segment = $key + 1;
if (isset($uri_segments[$uri_segment])) {
$offset = $uri_segments[$uri_segment];
}
unset($uri_segments[$key], $uri_segments[$key + 1]);
$base_url = site_url(implode('/', $uri_segments) . '/page/');
}
}
if (!$uri_segment) {
$base_url = site_url($this->uri->uri_string() . '/page/');
}
$config = array(
'base_url' => $base_url,
'uri_segment' => $uri_segment,
'total_rows' => $this->total_rows,
'per_page' => $per_page
);
if ($this->config->item('pagination_style')) {
$config = array_merge($config, $this->config->item('pagination_style'));
}
$this->pagination->initialize($config);
$this->page_links = $this->pagination->create_links();
/**
* Done with pagination, now on to the paged results
*/
$this->set_defaults();
foreach ($with as $method) {
$this->$method();
}
$this->db->limit($per_page, $offset);
$this->query = $this->db->get($this->table);
return $this;
}
function paginate_api($limit, $offset)
{
$this->set_defaults();
if (empty($limit)) {
$limit = $this->default_limit;
}
if (empty($offset)) {
$offset = 0;
}
$this->db->limit($limit, $offset);
return $this->db->get($this->table)->result();
}
/**
* Retrieves a single record based on primary key value.
*/
public function get_by_id($id, $with = array())
{
foreach ($with as $method) {
$this->$method();
}
return $this->where($this->primary_key, $id)->get()->row();
}
public function save($id = NULL, $db_array = NULL)
{
if (!$db_array) {
$db_array = $this->db_array();
}
if (!$id) {
if ($this->date_created_field) {
$db_array[$this->date_created_field] = time();
}
$this->db->insert($this->table, $db_array);
// $this->session->set_flashdata('alert_success', 'Record successfully created.');
return $this->db->insert_id();
} else {
if ($this->date_modified_field) {
$db_array[$this->date_modified_field] = time();
}
$this->db->where($this->primary_key, $id);
$this->db->update($this->table, $db_array);
// $this->session->set_flashdata('alert_success', 'Record successfully updated.');
return $id;
}
}
/**
* Returns an array based on $_POST input matching the ruleset used to
* validate the form submission.
*/
public function db_array()
{
$db_array = array();
$validation_rules = $this->{$this->validation_rules}();
foreach ($this->input->post() as $key => $value) {
if (array_key_exists($key, $validation_rules)) {
$db_array[$key] = $value;
}
}
return $db_array;
}
/**
* Deletes a record based on primary key value.
* $this->model_name->delete(5);
*/
public function delete($id, $others = array())
{
if (!empty($others)) {
foreach ($others as $k => $v) {
$this->db->where($k, $v);
}
} else {
$this->db->where($this->primary_key, $id);
}
return $this->db->delete($this->table);
// $this->session->set_flashdata('alert_success', 'Record successfully deleted.');
}
/**
* Returns the CI query result object.
* $this->model_name->get()->result();
*/
public function result()
{
return $this->query->result();
}
/**
* Returns the CI query row object.
* $this->model_name->get()->row();
*/
public function row()
{
return $this->query->row();
}
/**
* Returns CI query result array.
* $this->model_name->get()->result_array();
*/
public function result_array()
{
return $this->query->result_array();
}
/**
* Returns CI query row array.
* $this->model_name->get()->row_array();
*/
public function row_array()
{
return $this->query->row_array();
}
/**
* Returns CI query num_rows().
* $this->model_name->get()->num_rows();
*/
public function num_rows()
{
return $this->query->num_rows();
}
/**
* Used to retrieve record by ID and populate $this->form_values.
* #param int $id
*/
public function prep_form($id = NULL)
{
if (!$_POST and ($id)) {
$this->db->where($this->primary_key, $id);
$row = $this->db->get($this->table)->row();
foreach ($row as $key => $value) {
$this->form_values[$key] = $value;
}
}
}
/**
* Performs validation on submitted form. By default, looks for method in
* child model called validation_rules, but can be forced to run validation
* on any method in child model which returns array of validation rules.
* #param string $validation_rules
* #return boolean
*/
public function run_validation($validation_rules = NULL)
{
if (!$validation_rules) {
$validation_rules = $this->default_validation_rules;
}
foreach (array_keys($_POST) as $key) {
$this->form_values[$key] = $this->input->post($key);
}
if (method_exists($this, $validation_rules)) {
$this->validation_rules = $validation_rules;
$this->load->library('form_validation');
$this->form_validation->set_rules($this->$validation_rules());
$run = $this->form_validation->run();
$this->validation_errors = validation_errors();
return $run;
}
}
/**
* Returns the assigned form value to a form input element.
* #param type $key
* #return type
*/
public function form_value($key)
{
return (isset($this->form_values[$key])) ? $this->form_values[$key] : '';
}
}
then when i need a model for a specific database i use class inside models
models/content.php
class content_types extends MY_Model
{
function __construct()
{
parent::__construct();
$this->curentuserdata = $this->session->userdata('lg_result');
$this->userprofile = $this->session->userdata('user_profile');
}
public $table = 'content_types';
public $primary_key = 'id';
public $default_limit = 50;
public function default_order_by()
{
$this->db->order_by('id asc');
}
}
then where i need to call the model
$this->modelname->save($id,array());
$this->modelname->where()->get()->row();
so create one for the 'view table' and the others for insert tables