phppostpyrocms

How to pass information via button in table builder to a form builder?


To simplify, I have companies and workers attached to them in my pyrocms app. In the companies (created by the table builder in the admin panel automatically), there are buttons in the raw of each company. edit button is one of them provided out of the box. So I want to add another button, say 'Add Worker', and open the page of worker creation which I have done successfully. There is a multiple relation between companies and workers since a worker can work in multiple companies. You can think it as a categories of any post. The thing I want is, in the worker creation form page, I want to have the company whose 'Add worker' button is clicked appear in the 'working companies' field of worker automatically. What is the proper way of implementing such idea? I can pass HTML attributes to button using attributes but I don't know if it helps.

This is the $buttons array in the CompanyTableBuilder.

/**
 * The table buttons.
 * @var array|string
 */
protected $buttons = [
    'edit',
    'add_worker' => [
        'attributes' => [
            'href' => '/admin/crm/workers/create',
        ],
        'text' => 'Add worker',
        'type' => 'success',
    ]
];

Solution

  • Let's imagine you have two groups of clients: workers and payers

    class ClientTableBuilder extends \Anomaly\UsersModule\User\Table\UserTableBuilder
    {
    
        /**
         * The table model
         *
         * @var string
         */
        protected $model = UserModel::class;
    
        /**
         * The table views.
         *
         * @var array
         */
        protected $views = [
            'workers' => [
                'query'   => WorkersQuery::class,
                'buttons' => [
                    'jobs' => [
                        'type' => 'info',
                    ],
                ],
            ],
            'payers'  => [
                'query'   => PayersQuery::class,
                'buttons' => [
                    'payments' => [
                        'type' => 'info',
                    ],
                ],
            ],
        ];
    
    }
    

    When you would press the jobs button, you will go to admin/{addon}/clients/jobs/{user_id} route. So you would need to have the next one controller:

    class ClientsController extends AdminController
    {
    
        /**
         * Shows the clients list.
         *
         * @param   ClientTableBuilder  $table  The table
         * @return  Response
         */
        public function clients(ClientTableBuilder $table)
        {
            return $table->render();
        }
    
        /**
         * Shows the client's jobs list.
         *
         * @param   UserRepositoryInterface  $users  The users
         * @param   ClientJobsTableBuilder   $table  The table
         * @param                            $id     The identifier
         * @return  Response
         */
        public function assignedJobs(
            UserRepositoryInterface $users,
            ClientJobsTableBuilder $table,
            $id
        ) {
            /* @var UserInterface $user */
            if (!$user = $users->find($id)) {
                return $this->response->json([
                    'success' => false,
                    'message' => "Can't find user with id {$id}!",
                ]);
            }
    
            if ($this->request->ajax()) {
                $table->setAjax(true);
            }
    
            return $table->setUser($user)->render();
        }
    
        /**
         * Shows modal with unassigned jobs list
         *
         * @param   UserRepositoryInterface       $users  The users
         * @param   ClientJobsLookupTableBuilder  $table  The table
         * @param                                 $id     The identifier
         * @return  Response
         */
        public function unassignedJobs(
            UserRepositoryInterface $users,
            ClientJobsLookupTableBuilder $table,
            $id
        ) {
            /* @var UserInterface $user */
            if (!$user = $users->find($id)) {
                return $this->response->json([
                    'success' => false,
                    'message' => "Can't find user with id {$id}!",
                ]);
            }
    
            return $table->setUser($user)->render();
        }
    
        /**
         * Attach a job to a client
         *
         * @param   int|str       $user  The user's id
         * @param   int|str       $job   The job's id
         * @return  JsonResponse
         */
        public function attach($user, $job)
        {
            if ($error = $this->dispatch(new AttachJobToUser($user, $job))) {
                return $this->response->json([
                    'success' => false,
                    'message' => $error,
                ]);
            }
    
            return $this->response->json([
                'success' => true,
                'user'    => (int) $user,
                'job'     => (int) $job,
            ]);
        }
    
        /**
         * Detach a job from a client
         *
         * @param   int|str       $user  The user's id
         * @param   int|str       $job   The job's id
         * @return  JsonResponse
         */
        public function detach($user, $job)
        {
            if ($error = $this->dispatch(new DetachJobFromUser($user, $job))) {
                return $this->response->json([
                    'success' => false,
                    'message' => $error,
                ]);
            }
    
            return $this->response->json([
                'success' => true,
                'user'    => (int) $user,
                'job'     => (int) $job,
            ]);
        }
    }
    

    Then a value TB looks like:

    class ClientJobsTableBuilder extends ValueTableBuilder
    {
    
        /**
         * Table's user
         *
         * @var UserInterface|null
         */
        protected $user = null;
    
        /**
         * Table's columns
         *
         * @var array
         */
        protected $columns = [
            'name' => [
                'heading' => 'Name',
                'value'   => '<strong>{entry.name}</strong>',
            ],
            'type' => [
                'heading' => 'Type',
            ],
            'categories' => [
                'heading' => 'Categories',
                'value'   => 'entry.type.categories.pluck("name")|join("<br>")',
            ],
        ];
    
        /**
         * Table's buttons
         *
         * @var string
         */
        protected $buttons = ClientJobsTableButtons::class;
    
        /**
         * Table's actions
         *
         * @var array
         */
        protected $actions = [];
    
        /**
         * Table's options
         *
         * @var array
         */
        protected $options = [
            'sortable' => true,
        ];
    
        /**
         * Table's assets
         *
         * @var array
         */
        protected $assets = [
            'scripts.js' => [
                '{YOUR_MODULE_FULL_NS}::js/detach.js',
            ],
        ];
    
        /**
         * Gets the user.
         *
         * @return  UserInterface|null  The user.
         */
        public function getUser()
        {
            return $this->user;
        }
    
        /**
         * Sets the user.
         *
         * @param   UserInterface  $user  The user
         * @return  self
         */
        public function setUser(UserInterface $user)
        {
            $this->user = $user;
    
            return $this;
        }
    
    }
    

    then th valuetablebuttons:

    class ClientJobsTableButtons
    {
    
        /**
         * Handle the table buttons
         *
         * @param  ClientJobsTableBuilder  $builder  The builder
         */
        public function handle(ClientJobsTableBuilder $builder)
        {
            /* @var UserInterface $user */
            if (!$user = $builder->getUser()) {
                return;
            }
    
            $builder->setButtons([
                'detach' => [
                    'type'         => 'danger',
                    'data-detach'  => '{entry.id}',
                    'data-user'    => $user->getId(),
                    'data-dismiss' => 'multiple',
                ],
            ]);
        }
    
    }
    

    And the same shit for the lookup TB:

    class ClientJobsLookupTableBuilder extends TableBuilder
    {
    
        /**
         * AJAX mode flag
         *
         * @var bool
         */
        protected $ajax = true;
    
        /**
         * Table's user
         *
         * @var UserInterface|null
         */
        protected $user = null;
    
        /**
         * Table's columns
         *
         * @var array
         */
        protected $columns = [
            'name' => [
                'heading' => 'Name',
                'value'   => '<strong>{entry.name}</strong>',
            ],
            'type' => [
                'heading' => 'Type',
            ],
            'categories' => [
                'heading' => 'Categories',
                'value'   => 'entry.type.categories.pluck("name")|join("<br>")',
            ],
        ];
    
        /**
         * Table's buttons
         *
         * @var string
         */
        protected $buttons = ClientJobsLookupTableButtons::class;
    
        /**
         * Table's actions
         *
         * @var array
         */
        protected $actions = [];
    
        /**
         * Table's options
         *
         * @var array
         */
        protected $options = [
            'sortable' => false,
        ];
    
        /**
         * Table's assets
         *
         * @var array
         */
        protected $assets = [
            'scripts.js' => [
                '{YOUR_MODULE_FULL_NS}::js/attach.js',
            ],
        ];
    
        /**
         * Gets the user.
         *
         * @return  UserInterface|null  The user.
         */
        public function getUser()
        {
            return $this->user;
        }
    
        /**
         * Sets the user.
         *
         * @param   UserInterface  $user  The user
         * @return  self
         */
        public function setUser(UserInterface $user)
        {
            $this->user = $user;
    
            return $this;
        }
    
    }
    

    And lookup TB buttons:

    class ClientJobsLookupTableButtons
    {
    
        /**
         * Handle the table buttons
         *
         * @param  ClientJobsLookupTableBuilder  $builder  The builder
         */
        public function handle(ClientJobsLookupTableBuilder $builder)
        {
            /* @var UserInterface $user */
            if (!$user = $builder->getUser()) {
                return;
            }
    
            $builder->setButtons([
                'attach' => [
                    'data-attach'  => '{entry.id}',
                    'data-user'    => $user->getId(),
                    'data-dismiss' => 'multiple',
                ],
            ]);
        }
    
    }
    

    After that you would need only to write some JS for right behavior.

    UPD: This is the example of *Query class:

    class WorkersQuery
    {
    
        /**
         * Handle the query.
         *
         * @param  Builder                  $query  The query builder
         * @param  RoleRepositoryInterface  $roles  The roles repository
         */
        public function handle(Builder $query, RoleRepositoryInterface $roles)
        {
            /* @var RoleInterface $role */
            $role = $roles->findBySlug('worker');
    
            $query
                ->leftJoin(
                    'users_users_roles',
                    'users_users_roles.entry_id',
                    '=',
                    'users_users.id'
                )
                ->where('users_users_roles.related_id', $role->getId());
        }
    
    }
    

    And the example of AttachJobToUser command:

    class AttachJobToUser
    {
    
        /**
         * User's identifier
         *
         * @var mixed
         */
        protected $user;
    
        /**
         * Job's identifier
         *
         * @var mixed
         */
        protected $job;
    
        /**
         * Create a new instance of AttachJobToUser class
         *
         * @param $user
         * @param $job
         */
        public function __construct($user, $job)
        {
            $this->user = $user;
            $this->job  = $job;
        }
    
        /**
         * Handle the command
         *
         * @param   UserRepositoryInterface  $users  The users
         * @param   JobRepositoryInterface   $jobs   The jobs
         * @return  boolean|string
         */
        public function handle(
            UserRepositoryInterface $users,
            JobRepositoryInterface $jobs
        ) {
            /* @var UserInterface $user */
            if (!$user = $users->find($this->user)) {
                return "Can't find user with id '{$this->user}'";
            }
    
            /* @var JobInterface $job */
            if (!$job = $jobs->find($this->job)) {
                return "Can't find job with id '{$this->job}'";
            }
    
            if (!$job->addWorker($user)) {
                return "Can't attach a job with id '{$this->job}' to a worker with id '{$this->user}'";
            }
    
            return false;
        }
    
    }