Symfony Error: Attempted to call an undefined method named […] of class - php

I'm currently upgrading from Symfony 2.3 to 2.8 and are deprecating for the 3.0 update.
Under the link below, I rewrite Symfony\Component\Form\Extension\Core\ChoiceList\ChoiceList and Symfony\Component\Form\Extension\Core\ChoiceList\LazyChoiceList.
Among them, the following error occurred.
I wondered if I should add getChoices(), so I added it to ChoiceLoader.php, but I still got an error.
How should I solve it?
Also, choice_list will be abolished soon, so I plan to rewrite it.
https://github.com/symfony/symfony/blob/2.7/UPGRADE-2.7.md
Error
Attempted to call an undefined method named "getChoices" of class
"Symfony\Component\Form\ChoiceList\Factory\DefaultChoiceListFactory"
ChoiceList->ChoiceLoader.php
namespace Ahi\Sp\AdminBundle\Form\ChoiceList;
//Remove
//use Symfony\Component\Form\Extension\Core\ChoiceList\ChoiceList;
//use Symfony\Component\Form\Extension\Core\ChoiceList\LazyChoiceList;
//Add
use Symfony\Component\Form\ChoiceList\Factory\DefaultChoiceListFactory;
use Symfony\Component\Form\ChoiceList\Loader\ChoiceLoaderInterface;
class StaffChoiceLoader implements ChoiceLoaderInterface
{
private $staffService;
private $loginStaff;
private $currentStaff;
public function __construct($staffService, $loginStaff)
{
$this->staffService = $staffService;
$this->loginStaff = $loginStaff;
}
public function loadChoiceList($value = null)
{
// Get the same shop staff as the login staff
$staffs = $this->staffService->getStaffByShop($this->loginStaff->getShop());
// If the current staff is not included in the acquired staff (due to transfer etc.), add it to the end
if ($this->currentStaff && !array_search($this->currentStaff, $staffs)) {
$staffs[] = $this->currentStaff;
}
//Remove
//return new ChoiceList($staffs, $staffs);
//Add
return new DefaultChoiceListFactory($staffs, $staffs);
}
//Add
public function loadChoicesForValues(array $values, $value = null)
{
if (empty($choices)) {
return array();
}
$values = array();
foreach ($choices as $person) {
$values[] = (string) $staff->getId();
}
return $values;
}
public function loadValuesForChoices(array $choices, $value= null)
{
if (empty($values)) {
return array();
}
return $this->staffService->getStaffByShop($this->loginStaff->getShop());
}
public function getChoices()
{
}
}
ArticleType.php
//Remove
//$authorChoiceList = new StaffChoiceLoader($this->staffService, $options['login_staff']);
//Add
$factory = new DefaultChoiceListFactory();
$authorChoiceList = $factory->createListFromLoader(new StaffChoiceLoader($this->staffService, $options['login_staff']));
$builder->add("author", "entity", array(
"required" => true,
"class" => "AhiSpCommonBundle:Staff",
"choice_list" => $authorChoiceList,
"empty_value" => "Please select",
));

Related

Dirty Methods in Rails equivalent in PHP Codeignitor

I am new to PHP & Codeigniter but it was needed some kind of implementation in PHP.
Following are dirty methods are provided in rails framework by default, here person is model object representing row inside persons table.
person.name = 'Bob'
person.changed? # => true
person.name_changed? # => true
person.name_changed?(from: nil, to: "Bob") # => true
person.name_was # => nil
person.name_change # => [nil, "Bob"]
person.name = 'Bill'
person.name_change # => [nil, "Bill"]
I am interested in to & from specially, Please suggest whether it is possible with any way.
If you would consider Laravel's elquent framework you have a great deal of that functionality already.
Laravel Eloquent update just if changes have been made
It holds an array of the "original" values in the Model, and if any of them have been changed it will commit them to the database.
They also come with a lot of events you can plug into(beforeSave, afterSave, beforeCreate, afterCreate, validation rules, etc...) and they can be extended easily. It might be the closest compatible thing I can imagine you're looking for.
This is however not codeigniter, it relies on a different framework. If you're not dead set on codeigniter you might consider switching to a framework like Laravel or OctoberCMS depending on your needs.
Edit because you're stuck with codeigniter
You might wish to use a library like this one: https://github.com/yidas/codeigniter-model
It is then very easy to extend with some custom caching mechanisms.
The code below is something you could use as a basis for your own model implementation.
It has a very rudementary logic basis, but allows you to check on the dirty status and roll back any changes made to the model.
Note this this is very rudementary, and might even contain a few errors because I have not run this code. it's more a proof of concept to help you create a model that suits your needs.
class User extends CI_Model
{
public $table = 'users';
public $primaryKey = 'id';
public $attributes;
public $original;
public $dirty = [];
public $exists = false;
function __construct()
{
parent::Model();
}
public static function find($model_id)
{
$static = new static;
$query = $static->db->query("SELECT * FROM ' . $static->getTable() . ' WHERE ' . $this->getKeyName() . ' = ?", [$model_id]);
if($result->num_rows()) {
$static->fill($query->row_array());
$static->exists = true;
}
else {
return null;
}
return $static;
}
public function getKeyName()
{
return $this->primaryKey;
}
public function getKey()
{
return $this->getAttribute($this->getKeyName());
}
public function getTable()
{
return $this->table;
}
public function fill($attributes)
{
if(is_null($this->original)) {
$this->original = $attributes;
$this->dirty = $attributes;
}
else {
foreach($attributes as $key => $value) {
if($this->original[$key] != $value) {
$this->dirty[$key] = $value;
}
}
}
$this->attributes = $attributes;
}
public function reset()
{
$this->dirty = [];
$this->attributes = $this->original;
}
public function getAttribute($attribute_name)
{
return array_key_exists($attribute_name, $this->attributes) ? $this->attributes[$attribute_name] : null;
}
public function __get($key)
{
return $this->getAttribute($key);
}
public function __set($key, $value)
{
$this->setAttribute($key, $value);
}
public function setAttribute($key, $value)
{
if(array_key_exists($key, $this->original)) {
if($this->original[$key] !== $value) {
$this->dirty[$key] = $value;
}
}
else {
$this->original[$key] = $value;
$this->dirty[$key] = $value;
}
$this->attributes[$key] = $value;
}
public function getDirty()
{
return $this->dirty;
}
public function isDirty()
{
return (bool)count($this->dirty);
}
public function save()
{
if($this->isDirty()) {
if($this->exists)
{
$this->db->where($this->getKeyName(), $this->getKey());
$this->db->update($this->getTable(), $this->getDirty());
$this->dirty = [];
$this->original = $this->attributes;
}
else
{
$this->db->insert($this->getTable(), $this->getDirty());
$this->dirty = [];
$this->original = $this->attributes;
$this->attributes[$this->getKeyName()] = $this->db->insert_id();
$this->original[$this->getKeyName()] = $this->getKey();
$this->exists = true;
}
}
}
}
if($user = User::find(1)) {
$user->name = "Johnny Bravo";
$user->save();
}

Array to string conversion error -Slim Twig-

Setup: I have a slim application and I pulled in Illuminate DB and Twig view.
if (!$validator->passed()) {
$errors = $validator->errors();
$users = User::all();
return $this->view($response, 'auth.login', compact('errors','users'));
}
Problem: When I run the above code, I am able to retrieve the users variable in my view, but the errors variable throws the following error.
Notice: Array to string conversion in /Users/davidchen/Documents/sites/slim.com/vendor/twig/twig/lib/Twig/Environment.php(378) : eval()'d code
on line
70 Array
The errors variable returns a multidimensional array, below you'll find the result that I get from print_r($errors).
Array (
[username] => Array (
[0] => username already exists
)
[password] => Array (
[0] => password must consist of at least 6 characters
)
)
Here are my related project files:
Twig Setup File (app.php)
$c = $app->getContainer();
$capsule = new \Illuminate\Database\Capsule\Manager;
$capsule->addConnection($config['config']['db']);
$capsule->setAsGlobal();
$capsule->bootEloquent();
$c['db'] = function($c) use ($capsule){
return $capsule;
};
$c['view'] = function($c){
$options['cache']=false;
$view = new \Slim\Views\Twig(__DIR__."/../views", $options);
$view->addExtension(new \Slim\Views\TwigExtension(
$c->router,
$c->request->getUri()
));
$view->getEnvironment()->addGlobal('flash', $c->flash);
return $view;
};
$c['flash'] = function($c){
return new Slim\Flash\Messages();
};
Validator Class
namespace App\Models\Auth;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Capsule\Manager as DB;
use Carbon\Carbon;
use DateTime;
class Validator extends Model
{
protected $field_name,
$data,
$errors = [];
/*
* Main validator
*/
public function __construct($request, $fields = []){
$data = $request->getParams();
$this->data = $data;
foreach ($fields as $field => $constraints) {
$this->field_name = $field;
if (isset($data[$field])) {
$field_value = $data[$field];
foreach (explode("|", $constraints) as $constraint) {
$obj = explode(":", $constraint);
$function_name = $obj[0];
if (isset($obj[1])) {
if(method_exists($this, $function_name))
{
$this->$function_name($obj[1],$field_value);
}
}
}
}else{
if (strpos($constraints, 'required') !== false) {
$validator->report($validator->field_name.' field is requried');
}
}
}
return $this;
}
/*
* Object Interface Methods
*/
private function report($message){
$this->errors[$this->field_name][]= $message;
}
public function errors(){
return $this->errors;
}
public function passed(){
if (!count($this->errors)) {
return true;
}
}
/*
* Validation Rules
*/
public function max($length,$value){
if (strlen($value)>$length) {
$this->report("{$this->field_name} must consist of less than {$length} characters");
}
}
public function min($length,$value){
if (strlen($value)<$length) {
$this->report("{$this->field_name} must consist of atleast {$length} characters");
}
}
public function distinct($tableName,$value){
if (DB::table($tableName)->where($this->field_name, $value)->exists()) {
$this->report("{$this->field_name} already exists");
}
}
public function date($format,$date){
if (!preg_match("/\d{4}-\d{2}-\d{2}\b/",$date)) {
$this->report("incorrect {$this->field_name} values");
}else{
$d = DateTime::createFromFormat($format, $date);
if ($d && $d->format($format) !== $date) {
$this->report("{$this->field_name} format should be {$format}");
}
}
}
public function match($matchField,$value){
if (isset($this->data[$matchField])) {
$valueTwo = $this->data[$matchField];
if ($value !== $valueTwo) {
$this->report("{$this->field_name} does not match {$matchField}");
}
}else{
$this->report("{$matchField} is required");
}
}
public function format($type,$value){
switch ($type) {
case 'noWhiteSpace':
if (preg_match("/\s/",$value)) {
$this->report("{$this->field_name} may not contain any spaces");
}break;
case 'alpha':
if (preg_match("/[^a-zA-Z]/",$value)) {
$this->report("{$this->field_name} may only contain letters");
}break;
case 'alphaNum':
if (preg_match("/[^a-zA-Z0-9]/",$value)) {
$this->report("{$this->field_name} may only contain letters");
}break;
case 'email':
if (!filter_var($value, FILTER_VALIDATE_EMAIL)) {
$this->report("in correct {$this->field_name} format");
}break;
default:
# code...
break;
}
}
}
Base Controller
namespace App\Controllers;
/**
*
*/
class Controller
{
protected $c;
function __construct($container)
{
$this->c = $container;
}
public function view($response, $path,$variables = []){
$this->c->view->render($response, str_replace(".","/",$path).".twig", $variables);
}
public function pathFor($routeName,$data = []){
return $this->c->router->pathFor($routeName,$data);
}
}
Auth Controller
namespace App\Controllers\Auth;
use App\Models\User\User;
use App\Controllers\Controller;
use App\Models\Auth\Validator;
/**
*
*/
class AuthController extends Controller
{
public function getLogin($request, $response){
return $this->view($response, 'auth.login');
}
public function postLogin($request, $response){
$validator = new Validator($request,[
'username'=>'required|min:3|max:64|format:alphaNum|distinct:users',
'password'=>'required|min:6|max:64|',
]);
if (!$validator->passed()) {
$errors = $validator->errors();
$users = User::all();
return $this->view($response, 'auth.login', compact('errors','users'));
}
return $this->view($response, 'home.login');
}
}
login.twig file
login.twig file
Hope one of you can shed some light on this problem. I've been struggling with this all morning.
You could try to loop over each item in a sequence. For example, to display a list of users provided in a variable called users:
<h1>Members</h1>
<ul>
{% for user in users %}
<li>{{ user.username|e }}</li>
{% endfor %}
</ul>
Read more

How to use SplObserver for Hook system?

I code a class for Hook system. But this is outdated. I want to use splObserver to code it.
<?php
class Event
{
private static $filters = [];
private static $actions = [];
public static function addAction($name, $callback, $priority = 10)
{
if (! isset(static::$actions[$name])) {
static::$actions[$name] = [];
}
static::$actions[$name][] = [
'priority' => (int)$priority,
'callback' => $callback,
];
}
public function doAction($name, ...$args)
{
$actions = isset(static::$actions[$name]) ? static::$actions[$name] : false;
if (! $actions) {
return;
}
// sort actions by priority
$sortArr = array_map(function ($action) {
return $action['priority'];
}, $actions);
\array_multisort($sortArr, $actions);
foreach ($actions as $action) {
\call_user_func_array($action['callback'], $args);
}
}
}
Event::addAction('action1', function(){
echo 'balabala1';
});
Event::addAction('action1', function(){
echo 'balabala2';
});
Event::doAction('action1');
Output: balabala1 balabala2
It works good.
I want to use SplObserver to re-code it and try to code but no idea.
I don't really know whether this implementation could be useful in a real life application or not but, for the sake of answering your question, here we go...
Let's imagine we have a User class that we'd like to hook with our custom functions.
First, we create a reusable trait containing the Subject logic, capable of managing "event names" to whom we can hook our actions.
trait SubjectTrait {
private $observers = [];
// this is not a real __construct() (we will call it later)
public function construct()
{
$this->observers["all"] = [];
}
private function initObserversGroup(string $name = "all")
{
if (!isset($this->observers[$name])) {
$this->observers[$name] = [];
}
}
private function getObservers(string $name = "all")
{
$this->initObserversGroup($name);
$group = $this->observers[$name];
$all = $this->observers["all"];
return array_merge($group, $all);
}
public function attach(\SplObserver $observer, string $name = "all")
{
$this->initObserversGroup($name);
$this->observers[$name][] = $observer;
}
public function detach(\SplObserver $observer, string $name = "all")
{
foreach ($this->getObservers($name) as $key => $o) {
if ($o === $observer) {
unset($this->observers[$name][$key]);
}
}
}
public function notify(string $name = "all", $data = null)
{
foreach ($this->getObservers($name) as $observer) {
$observer->update($this, $name, $data);
}
}
}
Next, we use the trait in our SplSubject User class:
class User implements \SplSubject
{
// It's necessary to alias construct() because it
// would conflict with other methods.
use SubjectTrait {
SubjectTrait::construct as protected constructSubject;
}
public function __construct()
{
$this->constructSubject();
}
public function create()
{
// User creation code...
$this->notify("User:created");
}
public function update()
{
// User update code...
$this->notify("User:updated");
}
public function delete()
{
// User deletion code...
$this->notify("User:deleted");
}
}
The last step is to implement a reusable SplObserver. This observer is able to bind himself to a Closure (anonymous function).
class MyObserver implements SplObserver
{
protected $closure;
public function __construct(Closure $closure)
{
$this->closure = $closure->bindTo($this, $this);
}
public function update(SplSubject $subject, $name = null, $data = null)
{
$closure = $this->closure;
$closure($subject, $name, $data);
}
}
Now, the test:
$user = new User;
// our custom functions (Closures)
$function1 = function(SplSubject $subject, $name, $data) {
echo $name . ": function1\n"; // we could also use $data here
};
$function2 = function(SplSubject $subject, $name, $data) {
echo $name . ": function2\n";
};
// subscribe the first function to all events
$user->attach(new MyObserver($function1), 'all');
// subscribe the second function to user creations only
$user->attach(new MyObserver($function2), 'User:created');
// run a couple of methods to see what happens
$user->create();
$user->update();
The output will be:
User:created: function2
User:created: function1
User:updated: function1
NOTE: we could use SplObjectStorage instead of an array, to store observers in the trait.

Joomla Comonent update data

I'm working on a Joomla 3 component. Currently I'm programming the backend. I'm having a form to add new data and it is working quite well. But when I want to update the data the component creates a new item instead of updating the existing.
I was searching for position which let Joomla know, that this is an update, but without success.
So my question: what is the information that makes Joomla updating the data?
My Code:
Table:ia.php
class mkTableia extends JTable
{
/**
* Constructor
*
* #param object Database connector object
*/
function __construct(&$db)
{
parent::__construct('#__tbl_ia', 'ID', $db);
}
}
Model: ia.php
class mkModelia extends JModelAdmin
{
public function getTable($type = 'ia', $prefix = 'mkTable', $config = array())
{
return JTable::getInstance($type, $prefix, $config);
}
public function getForm($data = array(), $loadData = true)
{
// Get the form.
$form = $this->loadForm('com_mk.ia', 'ia',
array('control' => 'jform', 'load_data' => $loadData));
if (empty($form))
{
return false;
}
return $form;
}
protected function loadFormData()
{
// Check the session for previously entered form data.
$data = JFactory::getApplication()->getUserState('com_mk.edit.ia.data', array());
if (empty($data))
{
$data = $this->getItem();
}
return $data;
}
}
View:view.html.php
class mkViewia extends JViewLegacy
{
/**
* display method of Hello view
* #return void
*/
public function display($tpl = null)
{
// get the Data
$form = $this->get('Form');
$item = $this->get('Item');
// Check for errors.
if (count($errors = $this->get('Errors')))
{
JError::raiseError(500, implode('<br />', $errors));
return false;
}
// Assign the Data
$this->form = $form;
$this->item = $item;
// Set the toolbar
$this->addToolBar();
// Display the template
parent::display($tpl);
}
/**
* Setting the toolbar
*/
protected function addToolBar()
{
$input = JFactory::getApplication()->input;
$input->set('hidemainmenu', true);
$isNew = ($this->item->ID == 0);
JToolBarHelper::title($isNew ? JText::_('COM_MK_MANAGER_MK_NEW')
: JText::_('COM_MK_MANAGER_MK_EDIT'));
JToolBarHelper::save('IA.save');
JToolBarHelper::cancel('IA.cancel', $isNew ? 'JTOOLBAR_CANCEL'
: 'JTOOLBAR_CLOSE');
}
}

ZF2 Get params in factory

I have a dynamic category navigation. In the navigation factory I want to get a param from the route. How can I do this?
In my view
<?php echo $this->navigation('CategoryNavigation')->menu()->setUlClass('list-group'); ?>
In my module.php:
public function getServiceConfig()
{
return array(
'factories' => array(
'CategoryNavigation' => 'Application\Navigation\CategoryNavigation',
)
);
}
This is my navigation factory. I need to get the slug from the route where {{ store slug }} (2x) is defined.
<?php
namespace Application\Navigation;
use Zend\ServiceManager\ServiceLocator;
use Zend\ServiceManager\ServiceLocatorInterface;
use Zend\Navigation\Service\DefaultNavigationFactory;
class CategoryNavigation extends DefaultNavigationFactory
{
protected $sl;
protected function getPages(ServiceLocatorInterface $serviceLocator)
{
if (null === $this->pages) {
$em = $serviceLocator->get('Doctrine\ORM\EntityManager');
$storerepository = $em->getRepository('ApplicationShared\Entity\Store');
$store = $storerepository->findOneBy(array('slug' => '{{ store slug }}'));
$fetchMenu = $store->getCategories();
$configuration['navigation'][$this->getName()] = $this->buildNavigation($fetchMenu);
if (!isset($configuration['navigation'])) {
throw new Exception\InvalidArgumentException('Could not find navigation configuration key');
}
if (!isset($configuration['navigation'][$this->getName()])) {
throw new Exception\InvalidArgumentException(sprintf(
'Failed to find a navigation container by the name "%s"',
$this->getName()
));
}
$application = $serviceLocator->get('Application');
$routeMatch = $application->getMvcEvent()->getRouteMatch();
$router = $application->getMvcEvent()->getRouter();
$pages = $this->getPagesFromConfig($configuration['navigation'][$this->getName()]);
//
$this->pages = $this->injectComponents($pages, $routeMatch, $router);
}
return $this->pages;
}
protected function buildNavigation($elements, $parentid = null){
foreach ($elements as $index => $element) {
if (!$element->getParent()) {
$branch[$element->getCategoryName()] = $this->navigationItem($element);
}
}
return $branch;
}
protected function navigationItem($element){
$branch['label'] = $element->getCategoryName();
$branch['route'] = 'store/type/catalog';
$branch['params'] = array('slug' => '{{ store slug }}','category' => $element->getSlug());
if(count($element->getChildren()) > 0){
foreach($element->getChildren() as $element){
$branch['pages'][] = $this->navigationItem($element);
}
}
return $branch;
}
}
Or are there better way's to achieve this?
Searching for a few hours, in the end asking it on stack, and now I find the answer in a half hour.
I just had to ad this in my factory
$router = $serviceLocator->get('router');
$request = $serviceLocator->get('request');
// Get the router match
$routerMatch = $router->match($request);
$this->slug = $routerMatch->getParam("slug");
Or the following for ZF3:
$container->get('Application')->getMvcEvent()->getRouteMatch()->getParam('whateverYouWant', false);

Categories