In CodeIgniter 3 was ability to get all language lines as array with:
$this->lang->language
It is very useful for views - no need to list all the required language fields and just add all of them (from the loaded language files).
Is it possible to do that in CodeIgniter 4?
Here is my Solution.
Create an App\Libraries Class and extends Language.
App\Libraries\I18n.php
<?php
namespace App\Libraries;
use CodeIgniter\Language\Language;
class I18n extends Language {
public function GetAllLanguage($with_class = false) {
$result = array();
$lang_arr = $this->language[$this->locale];
if ($with_class == true) {
return $lang_arr;
}
foreach ($lang_arr as $key => $value) {
$result += $lang_arr[$key];
}
return $result;
}
}
you can add method or something in this Libraries Class.
then Add this code in app\Config\Services.php
<?php
namespace Config;
use CodeIgniter\Config\BaseService;
use Config\Services as AppServices;
use Locale;
class Services extends BaseService
{
//override Language
//source code from system\Config\Services.php
public static function Language($locale = null, $getShared = true) {
if ($getShared) {
return static::getSharedInstance('language', $locale)->setLocale($locale);
}
if (AppServices::request() instanceof IncomingRequest) {
$requestLocale = AppServices::request()->getLocale();
} else {
$requestLocale = Locale::getDefault();
}
// Use '?:' for empty string check
$locale = $locale ?: $requestLocale;
return new \App\Libraries\I18n($locale); //override use Libraries
}
}
then you can use in controller.
when I have two Language file.
( Language\zh-TW\Menu.php 、 Language\zh-TW\Text.php)
$lang = \Config\Services::Language();
$lang->GetAllLanguage();
/**
array(157) {
["Login"] => "登入",
["BaseData"] => "基本資料",
...
["refresh"] => "刷新",
["AccountInfo"] => "使用者資訊",
["system_msg"] => "系統訊息",
...
}
**/
$lang = \Config\Services::Language();
$lang->GetAllLanguage(true);
/**
array(2) {
["Menu"]=>
array(26) {
["Login"] => "登入",
["BaseData"] => "基本資料"
...
},
["Text"]=>
array(131) {
["refresh"] => "刷新",
["AccountInfo"] => "使用者資訊",
["system_msg"] => "系統訊息",
...
}
}
**/
that it work.
Related
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",
));
A lot of time, I've had reasons to get the names of the relationships defined on an Eloquent object. Since Laravel currently provides no way/helper to do this, I came up with the below:
public static function getRelationshipsForEloquentlModel($eloquentObject) {
$methods = self::getDirectClassMethods($eloquentObject);
$relations = [];
foreach ($methods as $method) {
try {
$reflection = new \ReflectionMethod($eloquentObject, $method);
//filter out non-eloquent relationship methods that expect parameters in
//their signature (else they blow up when they get called below without pars)
$pars = $reflection->getNumberOfParameters();
if ($pars == 0) {
$possibleRelationship = $eloquentObject->$method();
//one of the things we can use to distinctively identify an eloquent
//relationship method (BelongsTo, HasMany...etc) is to check for
//one of the public methods defined in Illuminate/Database/Eloquent/Relations/Relation.php
//(and hope that it is not discontinued/removed in future versions of Laravel :))
if (method_exists($possibleRelationship, "getEager")) {
$relationshipType = get_class($possibleRelationship);
//remove namespace
if ($pos = strrpos($relationshipType, '\\')) {
$relationshipType = substr($relationshipType, $pos + 1);
}
$relations[$method] = $relationshipType;
}
}
} catch (\Exception $ex) {
//Eloquent's save() method will throw some
//sql error because $eloquentObject may be
//an empty object like new App\User (so some NOT NULL db fields may blow up)
}
}
return $relations;
}
And the helper class getDirectClassMethods is below (courtesy of onesimus on official PHP docs comment):
public static function getDirectClassMethods($class) {
$array1 = get_class_methods($class);
if ($parent_class = get_parent_class($class)) {
$array2 = get_class_methods($parent_class);
$array3 = array_diff($array1, $array2);
} else {
$array3 = $array1;
}
return ($array3);
}
Now this whole code listing looks so cumbersome and verbose to me, at least when the desired task is such a simple one. Is there a better/faster/more efficient way of achieving this without all these verbosity?
This trait should help
namespace App;
use ErrorException;
use Illuminate\Database\Eloquent\Relations\Relation;
use ReflectionClass;
use ReflectionMethod;
trait RelationshipsTrait
{
public function relationships() {
$model = new static;
$relationships = [];
foreach((new ReflectionClass($model))->getMethods(ReflectionMethod::IS_PUBLIC) as $method)
{
if ($method->class != get_class($model) ||
!empty($method->getParameters()) ||
$method->getName() == __FUNCTION__) {
continue;
}
try {
$return = $method->invoke($model);
if ($return instanceof Relation) {
$relationships[$method->getName()] = [
'type' => (new ReflectionClass($return))->getShortName(),
'model' => (new ReflectionClass($return->getRelated()))->getName()
];
}
} catch(ErrorException $e) {}
}
return $relationships;
}
}
You should get an array of arrays, just add the trait to any models.
class Article extends Model
{
use RelationshipsTrait;
...
}
$article = new Article;
dd($article->relationships());
Should output
"example" => array:2 [▼
"type" => "BelongsTo"
"model" => "App\Example"
],
"gallery" => array:2 [▼
"type" => "MorphMany"
"model" => "App\Gallery"
]
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
Inside my processor class I have a statement that grabs all the projects from a db table and formats them to be displayed. This method does not work and halts at the getCollection call.
class GlobalLinkSettingsProcessor extends modObjectGetListProcessor{
public function initialize() {
return parent::initialize();
}
public function process() {
$result = $this->modx->getCollection('ManagerProjects');
$project_names = array();
foreach ($result as $row) {
$projects = unserialize($row->get('manager_projects'));
foreach($projects as $short_code => $project) {
$project_names[] = array('project_name' => $project, 'project_short_code' => $short_code);
}
}
return '{"total":' . count($project_names) . ',"results":' . $this->modx->toJSON($project_names) . ',"success":true}';
}
...
}
This code that uses plain SQL does work:
class GlobalLinkSettingsProcessor extends modObjectGetListProcessor{
public function initialize() {
return parent::initialize();
}
public function process() {
$leadersql = "SELECT * FROM `modx_manager_projects`";
$query = $this->modx->query($leadersql);
$project_names = array();
while ($row = $query->fetch(PDO::FETCH_ASSOC)) {
$projects = unserialize($row['manager_projects']);
foreach($projects as $short_code => $project) {
$project_names[] = array('project_name' => $project, 'project_short_code' => $short_code);
}
};
return '{"total":' . count($project_names) . ',"results":' . $this->modx->toJSON($project_names) . ',"success":true}';
}
...
}
I use similar method to the first which saves ManagerProjects and works fine, so I don't think it has to do with the model declaration. I could easily just use the second method above since it seems to work, but I want to use the best method.
What is wrong with the first method?
Is the first method the proper way to implement SQL in the Modx processor? Or is there a better way?
We can do this task easier a little bit.
#Vasis is right but we can use base prepareRow method instead of reloading iterate method:
<?php
class GlobalLinkSettingsProcessor extends modObjectGetListProcessor{
public $classKey = 'ManagerProjects';
protected $projects = array();
public function prepareRow(xPDOObject $object) {
$_projects = unserialize($object->get('manager_projects'));
foreach($_projects as $short_code => $project) {
$this->projects[] = array('project_name' => $project, 'project_short_code' => $short_code);
}
return parent::prepareRow($object);
}
public function outputArray(array $array,$count = false) {
$count = count($this->projects);
return parent::outputArray($this->projects,$count);
}
}
return 'GlobalLinkSettingsProcessor';
There we can see one of modx ‘features’. In modObjectGetListProcessor process method we can see this:
public function process() {
$beforeQuery = $this->beforeQuery();
if ($beforeQuery !== true) {
return $this->failure($beforeQuery);
}
$data = $this->getData();
$list = $this->iterate($data);
return $this->outputArray($list,$data['total']);
}
getData method returns a list of objects and it goes to iterate method (where we can check if the object is accessible and change the list of these objects on demand). If you don't have access to some of objects we'll get changed list. And it goes to outputArray method but second parameter is still old for it. So you should count them again.
This is solution is quite well but you tried to get data which is stored in object's field. So afterIteration method will be unusable for further extension in my version of processor. But who cares? :)
P.S.: About your first version of processor. modObjectGetList processor is ready for getting collection. So you have not to use getcollection method. Just add proper classKey property to it.
Another way is in modProcessor extension. It gives to you a base structure. But you can make your own kind of stuff.
Because you do it wrong! Just see this. The right way to do it, is something like this:
<?php
class GlobalLinkSettingsProcessor extends modObjectGetListProcessor{
public $classKey = 'ManagerProjects';
public function iterate(array $data) {
$list = array();
$list = $this->beforeIteration($list);
$this->currentIndex = 0;
/** #var xPDOObject|modAccessibleObject $object */
foreach ($data['results'] as $object) {
if ($this->checkListPermission && $object instanceof modAccessibleObject && !$object->checkPolicy('list')) continue;
$projects = unserialize($object->get('manager_projects'));
foreach($projects as $short_code => $project) {
$objectArray = array('project_name' => $project, 'project_short_code' => $short_code);
if (!empty($objectArray) && is_array($objectArray)) {
$list[] = $objectArray;
$this->currentIndex++;
}
}
}
$list = $this->afterIteration($list);
return $list;
}
}
I am trying to change the behaviour of the Gedmo\Tree\RepositoryUtils->buildTree() method because I'd like to change the way the returned array is constructed.
I am trying to following:
I have a class:
<?php
namespace MyCorp\CMSBundle\Util;
use Doctrine\Common\Persistence\Mapping\ClassMetadata;
use Doctrine\Common\Persistence\ObjectManager;
use Gedmo\Exception\InvalidArgumentException;
/**
* Description of jsandjqTreeCompatibleRepositoryUtils
*
* #author peterrus
*/
class jsandjqTreeCompatibleRepositoryUtils extends Gedmo\Tree\RepositoryUtils {
public function buildTree(array $nodes, array $options = array()) {
$meta = $this->getClassMetadata();
$nestedTree = $this->repo->buildTreeArray($nodes);
$default = array(
'decorate' => false,
'rootOpen' => '<ul>',
'rootClose' => '</ul>',
'childOpen' => '<li>',
'childClose' => '</li>',
'nodeDecorator' => function ($node) use ($meta) {
// override and change it, guessing which field to use
if ($meta->hasField('title')) {
$field = 'title';
} elseif ($meta->hasField('name')) {
$field = 'name';
} else {
throw new InvalidArgumentException("Cannot find any representation field");
}
return $node[$field];
}
);
$options = array_merge($default, $options);
// If you don't want any html output it will return the nested array
if (!$options['decorate']) {
return $nestedTree;
}
if (!count($nestedTree)) {
return '';
}
$build = function($tree) use (&$build, &$options) {
$output = is_string($options['rootOpen']) ? $options['rootOpen'] : $options['rootOpen']($tree);
foreach ($tree as $node) {
$output .= is_string($options['childOpen']) ? $options['childOpen'] : $options['childOpen']($node);
$output .= $options['nodeDecorator']($node);
if (count($node['children']) > 0) {
$output .= $build($node['children']);
}
$output .= $options['childClose'];
}
return $output . $options['rootClose'];
};
return $build($nestedTree);
}
}
?>
Now I am trying to use this class instead of the one that is used by default when calling
$pagerepo = $this->getDoctrine()->getRepository('MyCorpCMSBundle:Page');
By doing the following typecasting:
$pagerepo = (jsandjqTreeCompatibleRepositoryUtils) $this->getDoctrine()->getRepository('MyCorpCMSBundle:Page');
But as this is no java, this is not possible.
What am I doing wrong?
May be a little late, but i needed to to the same thing - here is the solution in case someone else needs it:
Your Repository Class:
namespace Acme\Model\Repository;
use Doctrine\ORM\EntityManager;
use Gedmo\Tree\Entity\Repository\NestedTreeRepository;
use Doctrine\ORM\Mapping\ClassMetadata;
use MyNamespace\GenericBundle\Repository\RepositoryUtils as MyRepositoryUtils;
/**
* Group Repository
*/
class CategoryRepository extends NestedTreeRepository
{
/**
* #param EntityManager $em
* #param ClassMetadata $class
*/
public function __construct(EntityManager $em, ClassMetadata $class)
{
parent::__construct($em, $class);
$this->repoUtils = new MyRepositoryUtils($this->_em, $this->getClassMetadata(), $this->listener, $this);
}
}
in the MyRepositoryUtils you can overwrite the buildTree method:
namespace MyNamespace\GenericBundle\Repository;
use Doctrine\Common\Persistence\Mapping\ClassMetadata;
use Doctrine\Common\Persistence\ObjectManager;
use Gedmo\Exception\InvalidArgumentException;
use Gedmo\Tree\RepositoryUtils as GedmoRepositoryUtils;
class RepositoryUtils extends GedmoRepositoryUtils
{
/**
* {#inheritDoc}
*/
public function buildTree(array $nodes, array $options = array())
{
}
}