...

Hi! I’m Starx

experienced Software Developer. And this is my blog
Start Reading About me
Blog Page
June 30, 2016

How to run Witter app from Udacity “Offline Web Applications” course using docker?

This blog post shows how you can run the Wittr application from a Udacity course called ‘Offline Web Applications’ using docker. This is helpful if your development stack does not run on localhost and you want to overcome `insecure origin` issue of the browsers.

Note: This is blog post, so I have written a bit back story as well. If you don’t want to read about that clone https://github.com/starx/wittr.git run docker-compose up -d Done!

I was trying a course from udacity called “Offline Web Applications” and I got stuck in the point where I was trying to register the service worker. That was because the request to register the service worker was coming form an insecure origin. Nowadays, modern browser browswer have started to deprecate powerful web feature on insecure origin (more here)) and the course fails to address this issue because the developer’s stack can vary. So the question is What is secure origin?

In chrome “Secure origins” are origins that match at least one of the following (scheme, host, port) patterns: more here

(https, *, *)
(wss, *, *)
(*, localhost, *)
(*, 127/8, *)
(*, ::1/128, *)
(file, *, —)
(chrome-extension, *, —) 

So it still supports developer in a way that is does not restrict request from localhost 127... or ::1. Which was another thing to fix because my development stack is not be the most common one as I don’t use localhost to test my work. I do these through vms, vagrant and nowadays from docker as well. This means that most of the time the test url is anything but localhost. So stuck on this for a while I tries several things, like trying to turn off insecure origin flag on flag, proxy, trying to point localhost to another ip is windows 10 bunch of bullshit which is just lost time. So I created a docker configuration to run the witter project.

file: Dockerfile

FROM node:latest

# Create app directory
RUN mkdir -p /usr/src/app
WORKDIR /usr/src/app

# Install app dependencies
COPY ./* /usr/src/app/
RUN npm install

# Bundle app source
COPY . /usr/src/app

EXPOSE 8888 8889
CMD [ "npm run serve" ]

Simple enough for those who know docker basics. It binds with the ports in the host, so the project will run as localhost:8888 and localhost:8889 as the course wants. But if you want to know what it does, I am happy to explain it.

I have forked the course repo and have added this (with a docker-compose.yml as well) to the project itself at:

https://github.com/starx/wittr

Clone https://github.com/starx/wittr.git rather than the one provided in the course and run docker-compose up -d and continue with the course. I have also have created a pull request to help others but there is already 2 previous pull request pending without any comments, So I doubt it will be pulled.

Comments appreciated.

Read more
June 8, 2016

How to restrict modification to selected properties of an entity while persisting?

Let’s suppose we have an entity class and a generic FormType corresponding to the entity

class Entry {
    protected $start_time;
    protected $end_time;
    protected $confirmed; // Boolean

    // ... Other fields and getters and setters
}

On the CRUD of this entity, you might not want to allow any modification on start_time or end_time if the entity has been confirmed or in the above case when $confirmed === true

Here are couple of ways this can be solved:

  1. Using the EntityManager you can retrieve original data of the entity. $em->getUnitOfWork()->getOriginalEntityData($entity) returns an array with old data of an entity which can be used to reset the data.

    if($form->isSubmitted() && $editForm->isValid()) {
        // The form was submitted
        if($confirmed === true) { // if the entity was confirmed previously
            $oldData = $em->getUnitOfWork()->getOriginalEntityData($entity);
            $entity->setStartTime($oldData['start_time']);
            $entity->setEndTime($oldData['end_time']);
        }
        // After this you can happily persist the data
        $em->persist($entity);
        $em->flush();
    }
    
  2. Using Form Events

    The following is a Symfony 3 Solution, try this solution for Symfony 2.

    class EntityType extends AbstractType
    {
        public function buildForm(FormBuilderInterface $builder, array $options)
        {
            // ....
            // ....
            // Your form fields
    
            $builder->addEventListener(FormEvents::POST_SET_DATA, array($this, 'onPreSetData'));
        }
    
    
        public function onPreSetData(FormEvent $event) {
            /** @var YourEntity $entity */
            $entity = $event->getData();
            $form = $event->getForm();
    
            if($entity instanceof YourEntity) {
                if ($entity->getTimesheetEntry()->getTimeApproved() === true) {
                    $config = $form->get('start_time')->getConfig();
                    $options = $config->getOptions();
                    $options['disabled'] = true;
                    $form->add('start_time', get_class($config->getType()->getInnerType()), $options);
    
                    $config = $form->get('end_time')->getConfig();
                    $options = $config->getOptions();
                    $options['disabled'] = true;
                    $form->add('end_time', get_class($config->getType()->getInnerType()), $options);
                }
            }
    
        }
    }
    
Read more
April 8, 2016

javascript switch statement/case expression

RGdent’s Question:

I am using a switch statement to search for undefined values to manually change it. but I am having trouble using boolean expressions as I would when using an if statement.

Like: if statemant

if(Item1 == undefined)
{
  item1 ="No";
}
else if (Item2 == undefined)
{
  item2 = "No";
}

etc..

I tried this with the switch statement:

 switch (array) {
 case (item1 == undefined):
 item1 = "No";
 console.log('item1 result', item1 );
 break;
 case item2 == undefined:
 item2 = "No";
 console.log('item2 result', item2 );
 break;
 default:

 }

It does not run through the switch statement, except for when I remove == undefined and only use item1. then it works?

The switch cannot evaluate values of the array like that and that is why it does not run through the switch statement. You need to define which value of that array you want to switch.

Inside case statement you also cannot use expression, you have to use a value there as well.

So, if you are dead set on using switch for what you are trying to accomplish, you can do something like this:

item1 = array[1];
switch(item1) {
    case "undefined":
        // so on
    break;
}

But, based on your example you are probably trying to check if the values are set or not, for that if statements are still the best choice rather than switch.

$arr = []; // Your array
if(typeof $arr[0] == "undefined") {
    $arr[0] = "No";
}
Read more
April 5, 2016

MVC Design – views with content for a usergroup

H0mebrewer’s Question:

I’m writing my first MVC application in PHP. I don’t want to use any framework.

Now I’m searching for a good solution or best practise to display elements for users in views, only if they are allowed to interact with the function that a specific element calls.

The app loads a template file in the view.
Within the template file I show the data from the model, which is passed from the controller to the view.

From controller

public function showUserList() {
    $userList = $this->model->getUserList();
    $this->view->loadTemplate($userList, 'user_list.php');
}

From view:

class user_view  {
    public function loadTemplate($data, $file, $buffer= false){
        if($buffer === true){
            ob_start();
            include dirname(__FILE__).'/templates/'.$file;
            $c = ob_get_contents();
            ob_end_clean();
            return $c;
        }else{
            include dirname(__FILE__).'/templates/'.$file;
        }
    }
}

Template:

<table class="table" id="data-table">
    <thead>
        <tr>
        <th>Name</th>
        <th>Username</th>
        <th>E-Mail</th>
        <th>Group</th>
        <th>Action</th>
        </tr>
    </thead>
    <tbody>
    <?php
       if(is_array($data)){

          foreach($data as $key => $value){
          ?>
            <tr>
                <td><?php echo $data['name'].' '.$data['surname']; ?></td>
                    <td><?php echo $data['abbr']; ?></td>
                    <td><?php echo $data['email']; ?></td>
                    <td><?php echo $data['gr_name']; ?></td>
                    <td>

                    <div data-access="," onclick="$user_controller.showUserChangeView('<?php echo $data['id']; ?>')" class="btn btn-primary">edit</div>
                    <div data-access="checkAccess" onclick="$user_controller.deleteUser('<?php echo $data['id']; ?>')" class="btn btn-danger">delete</div>

                    </td>
                </tr>

          <?php
          }

       }
    ?>
    </tbody>
</table>
</div>

I want to display buttons or menuitmes only, if a user is permitted to call the function of the button or the menuitem.

My first approach ist to create a template file with all elements which could be displayed. Elements which should not be accessible for all users get an data attribute.

The view loads the template, a DOM parser pareses the file, checks if the user is permitted to call that function with a data attribute.
If the user is not permitted to call that function, the element will be removed.
This is not yet implemented because I’m not sure if this is that a good solution or how I can do it even better?

My suggestion is to implement either ACL (Access Control List) or RBAC (Role Based Access Control) depending on what you find useful. I prefer RBAC so I will give a minimalistic example of one.

Create a list of all possible actions a user can make and define roles which might have acess to them.

// List of permissions (the actual term to define resources in RBAC) for the users
$actions = array(
    'view' => array(ROLE_USER, ROLE_OWNER, ROLE_MANAGER, ROLE_ADMIN),
    'edit' => array(ROLE_OWNER, ROLE_ADMIN),
    'delete' => array(ROLE_OWNER, ROLE_ADMIN),
    'change_pass' => array(ROLE_OWNER, ROLE_MANAGER, ROLE_ADMIN)
)

Now when you display the button for these actions, you can check if current user has any of the role which allows him to access the actions. If answer to that is “yes” then show them otherwise don’t show them at all.

$currentUserRole = '..'; //Retrieve however you want
foreach($actions as $allowed_roles) {
    if(in_array($currentUserRole, $allowed_roels)) {
        echo "<div ...>"; // Basically show the action
    }
    // If not carry on to next action with out showing
}

(Note: Not showing only might not be enough for it be secured, so we must also the check then when executing the action as well)

Update: You would the put the above code in the following part:

<table class="table" id="data-table">
    <thead>
        <tr>
        <th>Name</th>
        <th>Username</th>
        <th>E-Mail</th>
        <th>Group</th>
        <th>Action</th>
        </tr>
    </thead>
    <tbody>
    <?php
       if(is_array($data)){

          foreach($data as $key => $value){
          ?>
            <tr>
                <td><?php echo $data['name'].' '.$data['surname']; ?></td>
                    <td><?php echo $data['abbr']; ?></td>
                    <td><?php echo $data['email']; ?></td>
                    <td><?php echo $data['gr_name']; ?></td>
                    <td>
                        $currentUserRole = '..'; //Retrieve however you want
                        foreach($actions as $allowed_roles) {
                            if(in_array($currentUserRole, $allowed_roels)) {
                               echo "<div ...>"; // Basically show the action
                            }
                            // If not carry on to next action with out showing
                        }
                    </td>
                </tr>

          <?php
          }

       }
    ?>
    </tbody>
</table>
</div>
Read more
April 1, 2016

How to create time attribute in SQL?

Agus Maloco’s Question:

I tried to create a table this way:

create table attendance (
      userId char(10) primary key not null, 
      name varchar(35) not null, 
      date_attendance date not null, 
      start_time timestamp 'HH24:MI:SS', 
      finish_time timestamp 'HH24:MI:SS'
);

Am I right about creating the time fields this way or there is some better option?

Timestamp will hold both date and time. There is data type time as well, which might be better suited for your use case.

CREATE TABLE attendance (
      userId char(10) primary key not null, 
      name varchar(35) not null, 
      date_attendance date not null, 
      start_time time,
      finish_time time
);
Read more
March 31, 2016

Update schema for create longtext field on MySQL data base on symfony

Ehsan’s Question:

I want to update MySQL field from text to longtext using Doctrine schema.

Now my code is like this:

/**
 *@var string
 *@ORMColumn(name="head_fa", type="string", length=1000, nullable=true)
 */
private $head_fa;

/**
 *@var string
 *@ORMColumn(name="head_en", type="string", length=1000, nullable=true)
 */
private $head_en;

/**
 *@var string
 *@ORMColumn(name="body_fa", type="text", length=1000, nullable=true)
 */
private $body_fa;

/**
 *@var string
 *@ORMColumn(name="body_en", type="text", length=1000, nullable=true)
 */
private $body_en;

and the problem is when i change this field to this code

/**
 *@var string
 *@ORMColumn(name="head_fa", type="string", length=1000, nullable=true)
 */
private $head_fa;

/**
 *@var string
 *@ORMColumn(name="head_en", type="string", length=1000, nullable=true)
 */
private $head_en;

/**
 *@var string
 *@ORMColumn(name="body_fa", type="text", nullable=true)
 */
private $body_fa;

/**
 *@var string
 *@ORMColumn(name="body_en", type="text", nullable=true)
 */
private $body_en;

and run “php app/console doctrine:schema:update –force” command on console it said that “Nothing to update – your database is already in sync with the current entity metadata.” How to change this field to longtext on mysql database.

I do the same on different part of the project.
this is the code

/**
 * @ORMColumn(name="body", type="text", nullable=true)
 */
protected $body;

and after executing the “php app/console doctrine:schema:update –force” command on terminal this field is changed to longtext on MySQL database.

If you don’t specify a length parameter, it will automatically the column as LONGTEXT in MySQL.

Read more

How to make symfony forms support datetime-local input type?

Starx’s Question:

Most of the browsers are dropping support for datetime and also datetime-local as a valid input type. As of the time of writing this question, there are more support for support for datetime-local than datetime(which is almost non-existent).

If you building forms using Symfony’s form builder, it supports datetime but not datetime-local. So how would you make symfony form builder accept datetime-local input type and keep the rest of the functionality of the input type same?

One way this problem can be solved is, if we can change the text of input type to say datetime-local which can be done by overwriting the DateTimeType and using that.

<?php

namespace AppBundleComponentFormExtensionCoreType;

use SymfonyComponentFormFormInterface;
use SymfonyComponentFormFormView;

class DateTimeType extends SymfonyComponentFormExtensionCoreTypeDateTimeType
{
    /**
     * {@inheritdoc}
     */
    public function buildView(FormView $view, FormInterface $form, array $options)
    {
        $view->vars['widget'] = $options['widget'];

        // Change the input to a HTML5 datetime input if
        //  * the widget is set to "single_text"
        //  * the format matches the one expected by HTML5
        //  * the html5 is set to true
        if ($options['html5'] && 'single_text' === $options['widget'] && self::HTML5_FORMAT === $options['format']) {
            $view->vars['type'] = 'datetime-local';
        }
    }

}
Read more
March 28, 2016

listen to kernel.request event on Silex?

Lhassan Baazzi’s Question:

I want to listen to kernel.request event on Silex microframework http://silex.sensiolabs.org/documentation

How ?

You can access the dispatcher service as $app['dispatcher'] and you can see here how to use it. But I think you should use Silex’s before event instead as it’s called on kernel.request as well (here is a good expample how to use it).

UPDATE:

The links above are outdated. Before filters moved to a new middlewares section and here is how you can write one:

$app->before(function (Request $request) {
    // do what you want ...

    // if you want you can return a response so the controller won't be called
    // return new Response($content);

    // or just return null
});

There is a on($eventName, $callback, $priority = 0) method on silex application which you can use to listen to any event. On this particular case it will be as following.

$app->on(SymfonyComponentHttpKernelKernelEvents::REQUEST, function (SymfonyComponentHttpKernelEventGetResponseEvent $event) use ($app) {
    // Your actions
});
Read more
March 18, 2016

Symfony2 form collection not calling addxxx and removexxx even if 'by_reference' =&gt; false

Nick Denry’s Question:

I have the Customer entity and two one-to-many relations CustomerPhone and CustomerAddress.

The Customer entity has addPhone/removePhone and addAddress/removeAddress “adders”.

CustomerType collections options has ‘by_reference’ => false for both collections.

Entity functions addPhone/removePhone and addAddress/removeAddress not called after form submitted, so CustomerPhone and CustomerAddress have no parent id after persist.

Why could addPhone/removePhone and addAddress/removeAddress not called on form submit?

UPD 1.

After @Baig suggestion now I have addPhone/removePhone “adders” called, but addAddress/removeAddress not. Can’t get why because they are identical.

 # TestCustomerBundle/Entity/Customer.php

 /**
 * @var string
 *
 * @ORMOneToMany(targetEntity="CustomerPhone", mappedBy="customerId", cascade={"persist"}, orphanRemoval=true)
 */
private $phone;

/**
 * @var string
 *
 * @ORMOneToMany(targetEntity="CustomerAddress", mappedBy="customerId", cascade={"persist"}, orphanRemoval=true)
 */
private $address;

Same file “adders”

# TestCustomerBundle/Entity/Customer.php
/**
 * Add customer phone.
 *
 * @param Phone $phone
 */
public function addPhone(CustomerPhone $phone) {
    $phone->setCustomerId($this);
    $this->phone->add($phone);

    return $this;
}

/**
 * Remove customer phone.
 *
 * @param Phone $phone customer phone
 */
public function removePhone(CustomerPhone $phone) {
    $this->phone->remove($phone);
}
/**
 * Add customer address.
 *
 * @param Address $address
 */
public function addAddress(CustomerAddress $address) {
    $address->setCustomerId($this);
    $this->address->add($address);

    return $this;
}

/**
 * Remove customer address.
 *
 * @param Address $address customer address
 */
public function removeAddress(CustomerAddress $address) {
    $this->address->remove($address);
}

Relations:

# TestCustomerBundle/Entity/CustomerPhone.php
/**
 * @ORMManyToOne(targetEntity="Customer", inversedBy="phone")
 * @ORMJoinColumn(name="customer_id", referencedColumnName="id")
 **/
private $customerId;

#TestCustomerBundle/Entity/CustomerAddress.php
/**
 * @ORMManyToOne(targetEntity="Customer", inversedBy="address")
 * @ORMJoinColumn(name="customer_id", referencedColumnName="id")
 **/
private $customerId;

CustomerType form:

public function buildForm(FormBuilderInterface $builder, array $options)
{
    $builder
        ->add('name')
        ->add('phone', 'collection', array(
            'type' => new CustomerPhoneType(),
            'allow_add' => true,
            'allow_delete' => true,
            'by_reference' => false,
            'options' => array('label' => false)
        ))
        ->add('address', 'collection', array(
            'type' => new CustomerAddressType(),
            'allow_add' => true,
            'allow_delete' => true,
            'by_reference' => false,
            'options' => array('label' => false)
        ))
        ->add('submit', 'submit')
    ;
}

Controller.

# TestCustomerBundle/Controller/DefaultController.php

public function newAction(Request $request)
    {
        $customer = new Customer();
        // Create form.
        $form = $this->createForm(new CustomerType(), $customer);
        // Handle form to store customer obect with doctrine.
        if ($request->getMethod() == 'POST')
        {
            $form->bind($request);
            if ($form->isValid())
            {
                /*$em = $this->get('doctrine')->getEntityManager();
                $em->persist($customer);
                $em->flush();*/
                $request->getSession()->getFlashBag()->add('success', 'New customer added');
            }
        }
        // Display form.
        return $this->render('DeliveryCrmBundle:Default:customer_form.html.twig', array(
            'form' => $form->createView()
        ));
    }

UPD 2.
Test if addAddress called.

/**
     * Add customer address.
     *
     * @param Address $address
     */
    public function addAddress(Address $address) {
        jkkh; // Test for error if method called. Nothing throws.
        $address->setCustomerId($this);
        $this->address->add($address);        
    }

UPD 3.

CustomerAddressType.php

<?php

namespace DeliveryCrmBundleForm;

use SymfonyComponentFormAbstractType;
use SymfonyComponentFormFormBuilderInterface;
use SymfonyComponentOptionsResolverOptionsResolverInterface;

class CustomerAddressType extends AbstractType
{
    /**
     * @param FormBuilderInterface $builder
     * @param array $options
     */
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder
            ->add('street')
            ->add('house')
            ->add('building', 'text', ['required' => false])
            ->add('flat', 'text', ['required' => false])
        ;
    }

    /**
     * @param OptionsResolverInterface $resolver
     */
    public function setDefaultOptions(OptionsResolverInterface $resolver)
    {
        $resolver->setDefaults(array(
            'data_class' => 'DeliveryCrmBundleEntityCustomerAddress'
        ));
    }

    /**
     * @return string
     */
    public function getName()
    {
        return 'delivery_crmbundle_customeraddress';
    }
}

CustomerPhoneType.php

<?php

namespace DeliveryCrmBundleForm;

use SymfonyComponentFormAbstractType;
use SymfonyComponentFormFormBuilderInterface;
use SymfonyComponentOptionsResolverOptionsResolverInterface;

class CustomerPhoneType extends AbstractType
{
    /**
     * @param FormBuilderInterface $builder
     * @param array $options
     */
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder
            ->add('number')
        ;
    }

    /**
     * @param OptionsResolverInterface $resolver
     */
    public function setDefaultOptions(OptionsResolverInterface $resolver)
    {
        $resolver->setDefaults(array(
            'data_class' => 'DeliveryCrmBundleEntityCustomerPhone'
        ));
    }

    /**
     * @return string
     */
    public function getName()
    {
        return 'phone';
    }
}

This answer corresponds to Symfony 3, but I am sure this applies to Symfony 2 as well. Also this answer is more as a reference than addressing OP’s issue in particular (which I am not to clear)

On ..Symfony/Component/PropertyAccess/PropertyAccessor.php the method writeProperty is responsible for calling either setXXXXs or addXXX & removeXXXX methods.

So here is order on which it looks for the method:

  1. If the entity is array or instance of Traversable (which ArrayCollection is) then pair of

    • addEntityNameSingular()
    • removeEntityNameSingular()

      Source for reference:

      if (is_array($value) || $value instanceof Traversable) {
          $methods = $this->findAdderAndRemover($reflClass, $singulars);
      
          if (null !== $methods) {
              $access[self::ACCESS_TYPE] = self::ACCESS_TYPE_ADDER_AND_REMOVER;
              $access[self::ACCESS_ADDER] = $methods[0];
              $access[self::ACCESS_REMOVER] = $methods[1];
          }
      }
      
  2. If not then:

    1. setEntityName()
    2. entityName()
    3. __set()
    4. $entity_name (Should be public)
    5. __call()

      Source for reference:

      if (!isset($access[self::ACCESS_TYPE])) {
          $setter = 'set'.$camelized;
          $getsetter = lcfirst($camelized); // jQuery style, e.g. read: last(), write: last($item)
      
          if ($this->isMethodAccessible($reflClass, $setter, 1)) {
              $access[self::ACCESS_TYPE] = self::ACCESS_TYPE_METHOD;
              $access[self::ACCESS_NAME] = $setter;
          } elseif ($this->isMethodAccessible($reflClass, $getsetter, 1)) {
              $access[self::ACCESS_TYPE] = self::ACCESS_TYPE_METHOD;
              $access[self::ACCESS_NAME] = $getsetter;
          } elseif ($this->isMethodAccessible($reflClass, '__set', 2)) {
              $access[self::ACCESS_TYPE] = self::ACCESS_TYPE_PROPERTY;
              $access[self::ACCESS_NAME] = $property;
          } elseif ($access[self::ACCESS_HAS_PROPERTY] && $reflClass->getProperty($property)->isPublic()) {
              $access[self::ACCESS_TYPE] = self::ACCESS_TYPE_PROPERTY;
              $access[self::ACCESS_NAME] = $property;
          } elseif ($this->magicCall && $this->isMethodAccessible($reflClass, '__call', 2)) {
              // we call the getter and hope the __call do the job
              $access[self::ACCESS_TYPE] = self::ACCESS_TYPE_MAGIC;
              $access[self::ACCESS_NAME] = $setter;
          } else {
              $access[self::ACCESS_TYPE] = self::ACCESS_TYPE_NOT_FOUND;
              $access[self::ACCESS_NAME] = sprintf(
                  'Neither the property "%s" nor one of the methods %s"%s()", "%s()", '.
                  '"__set()" or "__call()" exist and have public access in class "%s".',
                  $property,
                  implode('', array_map(function ($singular) {
                      return '"add'.$singular.'()"/"remove'.$singular.'()", ';
                  }, $singulars)),
                  $setter,
                  $getsetter,
                  $reflClass->name
              );
          }
      }
      

To answer OP’s issue, based on the above mentioned information, the PropertyAccessor class of symfony is not able to read your addXX and removeXX method properly. The potential reason might be that is not identified as array or ArrayCollection which has to be done from the constructor of the entity

public function __construct() {
     $this->address = new ArrayCollection();
     // ....
}
Read more
...

Please fill the form - I will response as fast as I can!