├── .gitignore ├── src ├── Rapyd │ ├── Views │ │ ├── Macros.html.twig │ │ ├── Pagination.twig │ │ ├── DataGrid.twig │ │ ├── Error.twig │ │ └── Form.layout.twig │ ├── Model.php │ ├── Config │ │ └── routes.php │ ├── Widgets │ │ ├── WidgetBuilder.php │ │ ├── DataGrid.php │ │ ├── DataGrid │ │ │ └── Column.php │ │ ├── DataSet.php │ │ ├── DataForm.php │ │ └── Widget.php │ ├── Error.php │ ├── Helpers │ │ ├── Parser.php │ │ ├── Lang.php │ │ ├── Pagination.php │ │ ├── HTML.php │ │ └── Url.php │ ├── Event.php │ ├── Controller.php │ └── Application.php ├── Modules │ ├── Users │ │ ├── Views │ │ │ └── Count.twig │ │ ├── Models │ │ │ └── User.php │ │ ├── Config │ │ │ └── routes.php │ │ └── Controllers │ │ │ └── Users.php │ └── Demos │ │ ├── Controllers │ │ ├── Demo.php │ │ ├── Hello.php │ │ ├── Eloquent.php │ │ ├── Dataset.php │ │ ├── Tests.php │ │ ├── Datagrid.php │ │ ├── Forms.php │ │ └── Schema.php │ │ ├── Views │ │ ├── Grid.twig │ │ ├── Hello.twig │ │ ├── Schema.twig │ │ ├── Form.twig │ │ ├── Eloquent.twig │ │ ├── Set.twig │ │ ├── Index.twig │ │ └── Demo.layout.twig │ │ ├── Models │ │ ├── User.php │ │ ├── Comment.php │ │ └── Article.php │ │ └── Config │ │ └── routes.php └── App │ ├── Config │ ├── twig.php │ ├── routes.php │ ├── db.php │ ├── config.php │ └── hooks.php │ ├── Views │ ├── Home.twig │ ├── 404.twig │ ├── layout.twig │ └── error.twig │ └── Controllers │ └── Home.php ├── web ├── favicon.ico ├── .htaccess └── index.php ├── composer.json └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | /composer.lock 2 | /vendor/ -------------------------------------------------------------------------------- /src/Rapyd/Views/Macros.html.twig: -------------------------------------------------------------------------------- 1 | 2 | {# TODO #} 3 | -------------------------------------------------------------------------------- /src/Modules/Users/Views/Count.twig: -------------------------------------------------------------------------------- 1 | 2 | Totale: {{ count }} 3 | -------------------------------------------------------------------------------- /web/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zofe/rapyd-framework/HEAD/web/favicon.ico -------------------------------------------------------------------------------- /web/.htaccess: -------------------------------------------------------------------------------- 1 | RewriteEngine On 2 | RewriteCond %{REQUEST_FILENAME} !-f 3 | RewriteRule ^ index.php [QSA,L] -------------------------------------------------------------------------------- /src/Rapyd/Model.php: -------------------------------------------------------------------------------- 1 | '(/pag/\d+)?(/orderby/\w+/(asc|desc))?(/pag/\d+)?' 5 | )); 6 | -------------------------------------------------------------------------------- /src/Modules/Users/Models/User.php: -------------------------------------------------------------------------------- 1 | addRoutes(array( 6 | '/users' => $users . ':index', 7 | '/users/count' => $users . ':count', 8 | )); 9 | -------------------------------------------------------------------------------- /src/Modules/Demos/Controllers/Demo.php: -------------------------------------------------------------------------------- 1 | render('Index'); 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /src/App/Config/twig.php: -------------------------------------------------------------------------------- 1 | true, 5 | 'charset' => 'utf-8', 6 | //'cache' => realpath('../templates/cache'), 7 | 'auto_reload' => true, 8 | 'strict_variables' => false, 9 | 'autoescape' => true 10 | ); 11 | 12 | return $conf; 13 | -------------------------------------------------------------------------------- /src/Modules/Demos/Controllers/Hello.php: -------------------------------------------------------------------------------- 1 | render('Hello', array('somevar'=>'Hello World!')); 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /src/App/Config/routes.php: -------------------------------------------------------------------------------- 1 | addRoutes(array( 4 | '/' => 'Home:index', 5 | '/hello/:name' => 'Home:hello', 6 | '/test/qs' => 'Home:qs', 7 | '/test/ds' => 'Home:dataset', 8 | '/test/form' => 'Home:form', 9 | '/test/schema' => 'Home:schema', 10 | )); 11 | -------------------------------------------------------------------------------- /src/App/Config/db.php: -------------------------------------------------------------------------------- 1 | 'mysql', 6 | 'host' => '127.0.0.1', 7 | 'database' => 'rapyd_framework', 8 | 'username' => 'root', 9 | 'password' => '', 10 | 'charset' => 'utf8', 11 | 'collation' => 'utf8_unicode_ci', 12 | 'prefix' => '', 13 | ); 14 | 15 | return $conf; 16 | -------------------------------------------------------------------------------- /src/Rapyd/Widgets/WidgetBuilder.php: -------------------------------------------------------------------------------- 1 | widget = $classname; 13 | } 14 | 15 | public function createBuilder() 16 | { 17 | return new $this->widget; 18 | } 19 | 20 | } 21 | -------------------------------------------------------------------------------- /src/Rapyd/Error.php: -------------------------------------------------------------------------------- 1 | app->environment(); 11 | $env['slim.errors'] = fopen('/path/to/output', 'w'); 12 | 13 | // Call next middleware 14 | $this->next->call(); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/Modules/Demos/Views/Grid.twig: -------------------------------------------------------------------------------- 1 | 2 | {# Grid.twig #} 3 | 4 | {% extends 'Demo.layout.twig' %} 5 | 6 | 7 | {% block title %} DataGrid {% endblock %} 8 | 9 | {% block content %} 10 | 11 | {{ dg|raw }} 12 | 13 | {% endblock %} 14 | 15 | 16 | {% block code %} 17 |
18 | {{ source_code('/../../Modules/Demos/Controllers/Datagrid.php') }} 19 | {{ source_code('/../../Modules/Demos/Views/Grid.twig') }} 20 | {% endblock %} 21 | -------------------------------------------------------------------------------- /src/Modules/Demos/Controllers/Eloquent.php: -------------------------------------------------------------------------------- 1 | find(1); 14 | 15 | $this->render('Eloquent', array('article' => $article)); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/Modules/Demos/Views/Hello.twig: -------------------------------------------------------------------------------- 1 | 2 | {# Hello.twig #} 3 | 4 | {% extends 'Demo.layout.twig' %} 5 | 6 | 7 | {% block title %} Basic demo {% endblock %} 8 | 9 | 10 | {% block content %} 11 | 12 |

{{ somevar }}

13 | 14 | {% endblock %} 15 | 16 | {% block code %} 17 |
18 | {{ source_code('/../../Modules/Demos/Controllers/Hello.php') }} 19 | {{ source_code('/../../Modules/Demos/Views/Hello.twig') }} 20 | {% endblock %} 21 | -------------------------------------------------------------------------------- /src/Modules/Demos/Views/Schema.twig: -------------------------------------------------------------------------------- 1 | 2 | 3 | {% extends 'Demo.layout.twig' %} 4 | 5 | 6 | {% block title %}Schema Builder{% endblock %} 7 | 8 | 9 | {% block content %} 10 | 11 | This Controller Install/Refill some db table 12 | 13 | {% endblock %} 14 | 15 | 16 | 17 | {% block code %} 18 |
19 | {{ source_code('/../../Modules/Demos/Controllers/Schema.php') }} 20 | {{ source_code('/../../Modules/Demos/Views/Schema.twig') }} 21 | {% endblock %} 22 | -------------------------------------------------------------------------------- /src/Modules/Demos/Models/User.php: -------------------------------------------------------------------------------- 1 | hasMany('Modules\Demos\Models\Comment', 'user_id'); 14 | } 15 | 16 | public function articles() 17 | { 18 | return $this->hasMany('Modules\Demos\Models\Article', 'author_id'); 19 | } 20 | 21 | } 22 | -------------------------------------------------------------------------------- /src/Modules/Demos/Controllers/Dataset.php: -------------------------------------------------------------------------------- 1 | set->createBuilder(); 14 | $ds->setSource(Article::with("comments", "author")); 15 | $ds->setPagination(5); 16 | $ds->getSet(); 17 | 18 | $this->render('Set', array('ds' => $ds)); 19 | } 20 | 21 | } 22 | -------------------------------------------------------------------------------- /src/Modules/Demos/Models/Comment.php: -------------------------------------------------------------------------------- 1 | belongsTo('Modules\Demos\Models\Article', 'article_id'); 14 | } 15 | 16 | public function user() 17 | { 18 | return $this->belongsTo('Modules\Demos\Models\User', 'user_id'); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/Modules/Demos/Controllers/Tests.php: -------------------------------------------------------------------------------- 1 | false, 'autoescape' => false, 'optimizations' => 0)); 13 | echo $env->loadTemplate(' {{ title }} {{ subtitle }} ')->render(array('title' => 'foo', 'subtitle' => 'bar')); 14 | } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /src/Modules/Demos/Config/routes.php: -------------------------------------------------------------------------------- 1 | addRoutes(array( 6 | '/demo' => $demos . 'Demo:index', 7 | '/demo/hello' => $demos . 'Hello:index', 8 | '/demo/eloquent' => $demos . 'Eloquent:index', 9 | '/demo/schema' => $demos . 'Schema:index', 10 | '/demo/datagrid:widget'=> $demos .'Datagrid:index', 11 | '/demo/dataset:widget'=> $demos . 'Dataset:index', 12 | 13 | '/demo/forms' => $demos . 'Forms:index', 14 | 15 | '/test/twig' => $demos . 'Tests:twig', 16 | )); 17 | -------------------------------------------------------------------------------- /src/Modules/Demos/Models/Article.php: -------------------------------------------------------------------------------- 1 | hasMany('Modules\Demos\Models\Comment', 'article_id'); 14 | } 15 | 16 | public function author() 17 | { 18 | return $this->belongsTo('Modules\Demos\Models\User', 'author_id'); 19 | } 20 | 21 | public function getPublicAttribute($value) 22 | { 23 | return (bool) $value; 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /src/App/Views/Home.twig: -------------------------------------------------------------------------------- 1 | {% extends 'layout.twig' %} 2 | {% block content %} 3 |
4 | 9 |

Rapyd-framework 2.0

10 |
11 | 12 |
13 |
14 |

Congratulations! Rapyd is setup and working correclty.

15 | 16 | Getting started 17 |
18 | {% endblock %} 19 | -------------------------------------------------------------------------------- /src/Modules/Demos/Views/Form.twig: -------------------------------------------------------------------------------- 1 | 2 | {# Form.twig #} 3 | 4 | {% extends 'Demo.layout.twig' %} 5 | 6 | 7 | 8 | {% block title %} Forms {% endblock %} 9 | 10 | 11 | {% block content %} 12 | 13 | 14 | {{ form_start(testform) }} 15 | {{ form_widget(testform) }} 16 | {{ form_end(testform) }} 17 | 18 | 19 | {% endblock %} 20 | 21 | 22 | 23 | 24 | {% block code %} 25 | 26 |
27 | {{ source_code('/../../Modules/Demos/Controllers/Forms.php') }} 28 | {{ source_code('/../../Modules/Demos/Models/Article.php') }} 29 | {{ source_code('/../../Modules/Demos/Views/Form.twig') }} 30 | 31 | {% endblock %} 32 | -------------------------------------------------------------------------------- /src/Modules/Demos/Controllers/Datagrid.php: -------------------------------------------------------------------------------- 1 | grid->createBuilder(); 15 | $dg->setSource(Article::with("author")); 16 | $dg->setPagination(10); 17 | $dg->add('article_id',"ID", true); 18 | $dg->add('title',"title"); 19 | $dg->add('{{ article.author.firstname|lower }}',"author"); 20 | $dg->getGrid(); 21 | 22 | $this->render('Grid', array('dg' => $dg)); 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /src/Modules/Demos/Views/Eloquent.twig: -------------------------------------------------------------------------------- 1 | 2 | {# Eloquent.twig #} 3 | 4 | {% extends 'Demo.layout.twig' %} 5 | 6 | 7 | {% block title %} Eloquent {% endblock %} 8 | 9 | 10 | {% block content %} 11 | 12 | 13 |
{{ article.title }}
14 |

15 | {{ article.body }}
16 | by {{ article.author.firstname }} 17 |

18 | 19 | 20 | {% endblock %} 21 | 22 | 23 | 24 | {% block code %} 25 | 26 |
27 | {{ source_code('/../../Modules/Demos/Controllers/Eloquent.php') }} 28 | {{ source_code('/../../Modules/Demos/Models/Article.php') }} 29 | {{ source_code('/../../Modules/Demos/Views/Eloquent.twig') }} 30 | 31 | {% endblock %} 32 | -------------------------------------------------------------------------------- /web/index.php: -------------------------------------------------------------------------------- 1 | notFound(function () use ($app) { 17 | $app->render('404.twig'); 18 | }); 19 | $app->error(function (\Exception $e) use ($app) { 20 | $app->render('error.twig', array('e'=> $e, 'trace'=> debug_backtrace())); 21 | }); 22 | $app->run(); 23 | -------------------------------------------------------------------------------- /src/App/Config/config.php: -------------------------------------------------------------------------------- 1 | false, 5 | 'templates.path' => __DIR__ . '/../Views', 6 | 'controller.class_prefix' => '\\App\\Controllers', 7 | 'controller.method_suffix' => 'Action', 8 | 'controller.template_suffix' => 'twig', 9 | 'url_method' => 'uri', 10 | 'timezone' => 'Europe/Rome', 11 | 'languages' => array( 12 | array('name' => 'english', 'locale' => 'en_US', 'dateformat' => 'm/d/Y', 'segment' => ''), 13 | array('name' => 'italiano', 'locale' => 'it_IT', 'dateformat' => 'd/m/Y', 'segment' => 'it'), 14 | array('name' => 'française', 'locale' => 'fr_FR', 'dateformat' => 'd/m/Y', 'segment' => 'fr'), 15 | array('name' => 'česky', 'locale' => 'cs_CZ', 'dateformat' => 'd.m.Y', 'segment' => 'cs') 16 | ), 17 | ); 18 | 19 | return $conf; 20 | -------------------------------------------------------------------------------- /src/Rapyd/Views/Pagination.twig: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 28 | -------------------------------------------------------------------------------- /src/Modules/Users/Controllers/Users.php: -------------------------------------------------------------------------------- 1 | app->response(); 17 | $res['Content-Type'] = 'application/json'; 18 | $res->body($users->toJson()); 19 | } 20 | 21 | public function countAction() 22 | { 23 | //this is fluent? maybe.. 24 | $count = $this->app->db->table('users')->count(); 25 | var_dump($count); 26 | 27 | var_dump($this->app->db->select("select * from users")); 28 | die; 29 | $this->render('Count', array('count' => $count)); 30 | } 31 | 32 | public function capocchieAction() 33 | { 34 | echo 'meme'; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/Rapyd/Views/DataGrid.twig: -------------------------------------------------------------------------------- 1 | 2 | {# DataGrid.twig #} 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | {% for column in dg.columns %} 11 | 27 | {% endfor %} 28 | 29 | 30 | {% for row in dg.rows %} 31 | 32 | {% for cell in row %} 33 | 34 | {% endfor %} 35 | 36 | {% endfor %} 37 |
12 | {% spaceless %} 13 | {% if column.orderby %} 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | {{ column.label }} 22 | {% else %} 23 | {{ column.label }} 24 | {% endif %} 25 | {% endspaceless %} 26 |
{{ cell.value|raw }}
38 | 39 | 40 | 43 | -------------------------------------------------------------------------------- /src/Rapyd/Views/Error.twig: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | ERROR 6 | 32 | 33 | 34 |

An Error was encountered

35 |

36 | {{ error }} 37 |

38 | 39 |

40 | Visit the Home Page 41 |

42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "zofe/rapyd-framework", 3 | "type": "project", 4 | "description": "microframework for PHP 5.3 CRUD pattern", 5 | "homepage": "http://www.rapyd.com", 6 | "keywords": ["Rapyd", "Slim", "Eloquent", "Twig", "CRUD"], 7 | "license": "MIT", 8 | "authors": [ 9 | { 10 | "name": "Felice Ostuni", 11 | "email": "felice.ostuni@gmail.com", 12 | "homepage": "http://www.feliceostuni.com", 13 | "role": "Developer" 14 | } 15 | ], 16 | "require": { 17 | "php": ">=5.3.0", 18 | "slim/slim": "2.*", 19 | "slim/views": "0.1.*", 20 | "twig/twig": "1.*", 21 | "illuminate/database": "4.0.*", 22 | "symfony/form": "2.3.*", 23 | "symfony/validator": "2.3.*", 24 | "symfony/config": "2.3.*", 25 | "symfony/translation": "2.3.*", 26 | "symfony/twig-bridge": "2.3.*" 27 | }, 28 | "minimum-stability": "dev", 29 | "autoload": { 30 | "psr-0": { 31 | "App": "src/", 32 | "Rapyd": "src/", 33 | "Modules": "src/" 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/Rapyd/Helpers/Parser.php: -------------------------------------------------------------------------------- 1 | env = new \Twig_Environment(new \Twig_Loader_String(), array('cache' => false, 'autoescape' => false, 'optimizations' => 0)); 13 | 14 | //aggiungere una estensione o filtri custom per abilitare callback da controller/querybuilder/model/viste?? 15 | //studiare http://twig.sensiolabs.org/doc/advanced.html 16 | //es: $dg->column(' {{ name }} ',"NAME") 17 | //$this->env->addExtension($extension); 18 | } 19 | 20 | ////es: $dg->column('{{ name }}, {{ lastname }}',"NAME") 21 | public function render($pattern, $array) 22 | { 23 | return $this->env->render($pattern,$array); 24 | } 25 | 26 | public function variables($pattern) 27 | { 28 | if (preg_match_all("/\{\{ (\w+)(\|\w+)? \}\}/U", $pattern, $m)) { 29 | 30 | //$m = array_map('array_filter', $m); // Remove empty values 31 | array_shift($m); // Remove first index [0] 32 | 33 | return $m[0]; 34 | } 35 | 36 | return false; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/Modules/Demos/Views/Set.twig: -------------------------------------------------------------------------------- 1 | 2 | {# Set.twig #} 3 | 4 | {% extends 'Demo.layout.twig' %} 5 | 6 | 7 | {% block title %} DataSet {% endblock %} 8 | 9 | 10 | {% block content %} 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | {% for article in ds.data %} 22 | 23 |
24 |
{{ article.title }}
25 |

26 | {{ article.body }}
27 | by {{ article.author.firstname }} 28 |

29 | 30 | {% for comment in article.comments %} 31 |  {{ comment.comment|slice(0, 50) }}...
32 | {% endfor %} 33 | 34 |
35 | {% endfor %} 36 | 37 | 38 | {{ ds.links()|raw }} 39 | 40 | 41 | 42 | 43 | 44 | 45 | {% endblock %} 46 | 47 | 48 | 49 | {% block code %} 50 | 51 |
52 | {{ source_code('/../../Modules/Demos/Controllers/Dataset.php') }} 53 | {{ source_code('/../../Modules/Demos/Models/Article.php') }} 54 | {{ source_code('/../../Modules/Demos/Views/Set.twig') }} 55 | 56 | {% endblock %} 57 | -------------------------------------------------------------------------------- /src/App/Views/404.twig: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 404 Page Not Found 6 | 33 | 34 | 35 |

404 Page Not Found

36 |

37 | The page you are looking for could not be found. Check the address bar to ensure your URL is spelled correctly. If all else fails, you can visit our home page at the link below. 38 |
39 |
40 | Visit the Home Page 41 |

42 | 43 | 44 | -------------------------------------------------------------------------------- /src/Modules/Demos/Controllers/Forms.php: -------------------------------------------------------------------------------- 1 | form->createBuilder(); 18 | $form->add('title', 'text', 19 | array('constraints' => array( new NotBlank(), new Length(array('min'=>4))), 20 | 'attr' => array('placeholder'=>'article title')) 21 | ); 22 | $form->add('body', 'textarea', array('constraints' => array(new NotBlank()))); 23 | $form->add('public', 'checkbox', array('required' => false)); 24 | $form->add('save', 'submit'); 25 | $form->setData($article->attributesToArray()); 26 | $form = $form->getForm(); 27 | 28 | //there is a post? 29 | if ($this->app->request()->post($form->getName())) { 30 | 31 | $form->bind($this->app->request()->post($form->getName())); 32 | 33 | if ($form->isValid()) { 34 | //bind model with new values and save 35 | $article->setRawAttributes($form->getData()); 36 | $article->save(); 37 | } 38 | } 39 | 40 | $this->render('Form', array('testform' => $form->createView())); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/App/Views/layout.twig: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | {{ title }} 6 | 7 | 8 | 9 | 10 | 47 | 48 | 49 |
50 | {% block content %}{% endblock %} 51 |
52 | 55 |
56 | 57 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /src/Rapyd/Event.php: -------------------------------------------------------------------------------- 1 | render('Home', array('name' => 'Bello!')); 11 | } 12 | 13 | public function helloAction($name) 14 | { 15 | $this->render('Home', array('name' => $name)); 16 | } 17 | 18 | public function qsAction() 19 | { 20 | $this->app->response()->write($this->app->url->append('gino', 2)->append('dino', 2)->get() . "
"); 21 | $this->app->response()->write($this->app->url->replace('key', 'newkey')->get() . "
"); 22 | $this->app->response()->write($this->app->url->value('key') . "
"); 23 | } 24 | 25 | public function datasetAction() 26 | { 27 | $ds = new \Rapyd\Widgets\DataSet(); 28 | $ds->source("demo_users"); 29 | $ds->per_page = 10; 30 | $ds->num_links = 5; 31 | $ds->build(); 32 | $this->render('Dataset', array('ds' => $ds)); 33 | } 34 | 35 | public function schemaAction() 36 | { 37 | $schema = $this->app->db->getSchemaBuilder(); 38 | 39 | $schema->dropIfExists("capocchie"); 40 | $schema->table("capocchie", function ($table) { 41 | $table->create(); 42 | $table->increments('id'); 43 | $table->integer('category_id')->unsigned(); 44 | $table->string('capocchie_name'); 45 | $table->string('capocchie_lastname'); 46 | $table->text('abstract'); 47 | $table->timestamps(); 48 | }); 49 | } 50 | 51 | public function formAction() 52 | { 53 | $form = new \Illuminate\Html\FormBuilder(); 54 | $form->open($options); 55 | } 56 | 57 | } 58 | -------------------------------------------------------------------------------- /src/App/Views/error.twig: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 500 ERROR 6 | 35 | 36 | 37 |

A PHP Error was encountered

38 |

39 | Message: {{ e.message }}
40 | {% if e.file %}Filename: {{ e.file }}
{% endif %} 41 | Line Number: {{ e.line }} 42 |

43 | 44 | 45 | {% if true %} 46 | 47 |
48 | Backtrace:
49 | 
50 | {% for error in trace  %}
51 |     {% if  error.file %}
52 | Line: {{ error.line }} - {{ error.function }} - File: {{ error.file }}
53 |     {% endif %}
54 | {% endfor %}
55 | 
56 | {% endif %} 57 | 58 |

59 | Visit the Home Page 60 |

61 | 62 | 63 | 64 | -------------------------------------------------------------------------------- /src/App/Config/hooks.php: -------------------------------------------------------------------------------- 1 | hook('slim.before', function () use ($app) { 5 | $env = $app->environment(); 6 | 7 | //var_dump($app->config('languages')); 8 | //die; 9 | // setup default lang based on first in the list 10 | $lang = $availableLangs[0]; 11 | 12 | // if they are accessing the root, you could try and direct them to the correct language 13 | if ($env['PATH_INFO'] == '/') { 14 | if (isset($env['ACCEPT_LANGUAGE'])) { 15 | 16 | // try and auto-detect, find the language with the lowest offset as they are in order of priority 17 | $priority_offset = strlen($env['ACCEPT_LANGUAGE']); 18 | 19 | foreach ($availableLangs as $availableLang) { 20 | $i = strpos($env['ACCEPT_LANGUAGE'], $availableLang); 21 | if ($i !== false && $i < $priority_offset) { 22 | $priority_offset = $i; 23 | $lang = $availableLang; 24 | } 25 | } 26 | } 27 | } else { 28 | 29 | $pathInfo = $env['PATH_INFO'] . (substr($env['PATH_INFO'], -1) !== '/' ? '/' : ''); 30 | 31 | // extract lang from PATH_INFO 32 | foreach ($availableLangs as $availableLang) { 33 | $match = '/'.$availableLang; 34 | if (strpos($pathInfo, $match.'/') === 0) { 35 | $lang = $availableLang; 36 | $env['PATH_INFO'] = substr($env['PATH_INFO'], strlen($match)); 37 | 38 | if (strlen($env['PATH_INFO']) == 0) { 39 | $env['PATH_INFO'] = '/'; 40 | } 41 | } 42 | } 43 | } 44 | 45 | $app->view()->setLang($lang); 46 | $app->view()->setAvailableLangs($availableLangs); 47 | $app->view()->setPathInfo($env['PATH_INFO']); 48 | }); 49 | 50 | */ 51 | -------------------------------------------------------------------------------- /src/Rapyd/Views/Form.layout.twig: -------------------------------------------------------------------------------- 1 | {% extends 'form_div_layout.html.twig' %} 2 | 3 | 4 | {% block submit_widget %} 5 | {% spaceless %} 6 |
7 |
8 | {% set attr = attr|merge({'class': (attr.class|default('') ~ ' btn btn-default')|trim}) %} 9 | {% set type = type|default('submit') %} 10 | {{ block('button_widget') }} 11 |
12 |
13 | 14 | {% endspaceless %} 15 | {% endblock submit_widget %} 16 | 17 | 18 | {% block form_start %} 19 | {% set attr = attr|merge({'class': (attr.class|default('') ~ ' form-horizontal')|trim, 'role': 'form'}) %} 20 | {{ parent() }} 21 | {% endblock form_start %} 22 | 23 | 24 | {% block form_label %} 25 | {% spaceless %} 26 | {% if label is not sameas(false) %} 27 | {% if not compound %} 28 | {% set label_attr = label_attr|merge({'for': id}) %} 29 | {% endif %} 30 | {% if required %} 31 | {% set label_attr = label_attr|merge({'class': (label_attr.class|default('') ~ ' required')|trim}) %} 32 | {% endif %} 33 | {% set label_attr = label_attr|merge({'class': (label_attr.class|default('') ~ ' col-sm-2 control-label')|trim}) %} 34 | 35 | {% if label is empty %} 36 | {% set label = name|humanize %} 37 | {% endif %} 38 | 39 | {{ label|trans({}, translation_domain) }} 40 | {% endif %} 41 | {% endspaceless %} 42 | {% endblock form_label %} 43 | 44 | 45 | {% block form_widget_simple %} 46 | {% spaceless %} 47 | {% set type = type|default('text') %} 48 | {% set attr = attr|merge({'class': (attr.class|default('') ~ ' form-control')|trim}) %} 49 | 50 | {% endspaceless %} 51 | {% endblock form_widget_simple %} 52 | 53 | {% block textarea_widget %} 54 | {% spaceless %} 55 | {% set attr = attr|merge({'class': (attr.class|default('') ~ ' form-control')|trim}) %} 56 | 57 | {% endspaceless %} 58 | {% endblock textarea_widget %} 59 | 60 | {% block form_row %} 61 | {% spaceless %} 62 |
63 | {{ form_label(form) }} 64 |
65 | {{ form_widget(form) }} 66 | {{ form_errors(form) }} 67 |
68 |
69 | {% endspaceless %} 70 | {% endblock form_row %} 71 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | rapyd-framework 2 | =============== 3 | 4 | Project URL: [https://github.com/zofe/rapyd-framework/](https://github.com/zofe/rapyd-framework/) 5 | 6 | Rapyd is a PHP microframework for PHP 5.3 built on top of Slim Framework, Twig, Symfony Forms, Illuminate/Database, Twitter Bootstrap. 7 | 8 | In detail: 9 | 10 | - Rapyd extend Slim application to give you an MVC with modular separation framework 11 | - It use Twig as view template engine 12 | - Illuminate/Database (Fluent query and schema builder, and Eloquent ORM) as db engine 13 | - Symfony Forms as base of Form widgets 14 | - Bootstrap 3 as standard for html/css output 15 | - A Pool of presentation widgets (DataGrids, etc..) to let you develop CRUDL applications really fast. 16 | 17 | 18 | Felice Ostuni 19 | 20 | 21 | 22 | ## IMPORTANT NOTICE ## 23 | development of __rapyd-framerowk__ is now stopped. (FORK IT if you like, I can't give support). 24 | Reason: I focused development on the "laravel" version. 25 | Alternatives: 26 | if you need Slim/Twig/Eloquent take a look at: 27 | http://www.xsanisty.com/project/slim-starter 28 | 29 | In you need Slim _(or any other router)_ /Blade/Eloquent take a look at: 30 | http://github.com/zofe/deficient 31 | 32 | 33 | Development of __rapyd-laravel__ is now ative (FORK IT and help in development with PULL REQUEST). 34 | Rapyd is now reborn as standard laravel package: 35 | https://github.com/zofe/rapyd-laravel 36 | 37 | 38 | 39 | ## requirements ## 40 | 41 | - composer http://getcomposer.org/ 42 | - PHP 5.3+ 43 | 44 | ## install via composer ## 45 | 46 | ``` 47 | $ composer create-project -s dev zofe/rapyd-framework rapyd-framework 48 | ``` 49 | 50 | (you can also, fork on github, download, git clone.. etc) 51 | Remember to setup your vhost document-root to rapyd-framework/web 52 | 53 | 54 | 55 | 56 | You'll get: 57 | a simple mvc, a powerful query builder & orm, a great template engine, powerful forms: 58 | 59 | - Slim http://www.slimframework.com/ 60 | - Eloquent https://github.com/illuminate/database 61 | - Twig http://twig.sensiolabs.org/ 62 | - Symfony Forms http://symfony.com/doc/master/book/forms.html 63 | 64 | 65 | -- /web -- 66 | is the document root folder you should set this folder as document root in your vhost 67 | 68 | -- /src/App -- 69 | is where to develop your application (using MVC) 70 | for example using eloquent-orm for you model, twig for your views, and controllers that extends \Rapyd\Controller 71 | 72 | -- /src/Modules -- 73 | just if you need to split application in modules 74 | 75 | ## TO-DO ## 76 | 77 | 78 | - reimplement rapyd CRUD widgets: 79 | * dataform 80 | * datafilter 81 | * dataedit 82 | 83 | 84 | ## really old version demo, documentation & source ## 85 | 86 | Note: I'll try do reimplement this stuffs in curret version 87 | 88 | - https://code.google.com/p/rapyd-framework/ 89 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /src/Rapyd/Helpers/Lang.php: -------------------------------------------------------------------------------- 1 | $value) { 20 | if (strpos($subkey, $namespace) !== false) { 21 | $subkey = str_replace($namespace, '', $subkey); 22 | $array[$subkey] = $value; 23 | } 24 | } 25 | 26 | return $array; 27 | } 28 | if ($key == '' or !isset(self::$lang[$key])) { 29 | return $key; 30 | } 31 | 32 | $string = self::$lang[$key]; 33 | if ($args == '') { 34 | return $string; 35 | } elseif (is_array($args)) { 36 | return vsprintf($string, $args); 37 | } else { 38 | return sprintf($string, $args); 39 | } 40 | } 41 | 42 | public static function getLang($value='') 43 | { 44 | static $array = array(); 45 | static $set; 46 | static $current; 47 | 48 | if ($value=='array' && count($array) ) return $array; 49 | if ($value=='set' && !is_null($set)) return $set; 50 | if (in_array($value, array('locale','name','segment','index','dateformat')) && !is_null($current)) return $current[$value]; 51 | if ($value=='' && !is_null($current)) return $current; 52 | 53 | //no static cache? so cycle languages and uri and fill static vars 54 | $segments = array(); 55 | 56 | foreach (rpd::config("languages") as $lang) { 57 | if ($lang['segment'] == '') { 58 | $default = $lang; 59 | continue; 60 | } 61 | $segments[$lang['segment']] = $lang; 62 | } 63 | $current = $default; 64 | if (count($segments)>0) { //piu' di una lingua 65 | $set = '('.implode('|',array_keys($segments)).')'; 66 | if (preg_match('@^'.$set.'/?@i', url_helper::get_uri() , $match)) { 67 | $current = $segments[$match[1]]; 68 | } 69 | } 70 | 71 | $array = array_merge(array('default'=>$default), $segments); 72 | $curr = array_search($current, $array); 73 | $array[$curr]['is_current'] = true; 74 | 75 | if ($value=='array') return $array; 76 | if ($value=='set') return $set; 77 | if (in_array($value, array('locale','name','segment','index'))) return $current[$value]; 78 | return $current; 79 | } 80 | 81 | } 82 | -------------------------------------------------------------------------------- /src/Modules/Demos/Views/Index.twig: -------------------------------------------------------------------------------- 1 | 2 | {# Index.twig #} 3 | 4 | 5 | {% extends 'Demo.layout.twig' %} 6 | 7 | 8 | {% block title %} Rapyd-Framework Demos {% endblock %} 9 | 10 | 11 | {% block content %} 12 | 13 | 14 |

Running demo module

15 | 16 | 21 | 22 | 23 |

About Rapyd Framework

24 | 25 |

26 | Rapyd is a PHP microframework for PHP 5.3 built on top of Slim Framework, Twig, Symfony Forms, Illuminate/Database, Bootstrap 3. 27 | It implement MVC and CRUDL patterns.
28 |
29 | There is no much documentation about Rapyd, so this demo mudule is the way to find how to make things.
30 | Please do not hit me, Rapyd is built on top of well-documented projects.
31 |

32 | 33 | 34 |

How it works

35 | 42 | 43 | 44 |

Reference

45 |

46 | Slim Framework
47 | slim is basically a magnificent router, It's used as base of Rapyd/Application
48 | (a layer to implement Controllers and manage modules organization, routing etc.) 49 |

50 | 51 | 52 |

53 | Twig
54 | simply the best template engine (our Views are twig powered) 55 |

56 | 57 |

58 | Symfony Forms
59 | you can use Symfony Forms in rapyd, without using "Symfony" 60 |

61 | 62 |

63 | Illuminate/Database
64 | from laravel 4, It include DB Schema Builder, Fluent Query Builder, and the smart Eloquent ORM (our Models) 65 |

66 | 67 | 68 |

69 | Bootstrap
70 | one of the best HTML/CSS frameworks 71 |

72 | 73 | 74 | {% endblock %} 75 | -------------------------------------------------------------------------------- /src/Modules/Demos/Controllers/Schema.php: -------------------------------------------------------------------------------- 1 | dropDB(); 11 | $this->fillDB(); 12 | $this->render('Schema'); 13 | } 14 | 15 | protected function fillDB() 16 | { 17 | //illuminate/dtabase schema builder 18 | $schema = $this->app->db->getSchemaBuilder(); 19 | 20 | //tables are already there 21 | if ($schema->hasTable("demo_users")) return; 22 | 23 | //create all tables 24 | $schema->table("demo_users", function ($table) { 25 | $table->create(); 26 | $table->increments('user_id'); 27 | $table->string('firstname', 100); 28 | $table->string('lastname', 100); 29 | $table->timestamps(); 30 | }); 31 | $schema->table("demo_articles", function ($table) { 32 | $table->create(); 33 | $table->increments('article_id'); 34 | $table->integer('author_id')->unsigned(); 35 | $table->string('title', 200); 36 | $table->text('body'); 37 | $table->boolean('public'); 38 | $table->timestamps(); 39 | }); 40 | $schema->table("demo_comments", function ($table) { 41 | $table->create(); 42 | $table->increments('comment_id'); 43 | $table->integer('user_id')->unsigned(); 44 | $table->integer('article_id')->unsigned(); 45 | $table->text('comment'); 46 | $table->timestamps(); 47 | }); 48 | 49 | //populate all tables 50 | $users = $this->app->db->table('demo_users'); 51 | $users->insert(array('firstname' => 'Jhon', 'lastname' => 'Doe')); 52 | $users->insert(array('firstname' => 'Jane', 'lastname' => 'Doe')); 53 | 54 | $articles = $this->app->db->table('demo_articles'); 55 | for ($i=1; $i<=20; $i++) { 56 | $articles->insert(array( 57 | 'author_id' => rand(1,2), 58 | 'title' => 'Article '.$i, 59 | 'body' => 'Body of article '.$i, 60 | 'public' => true,) 61 | ); 62 | } 63 | 64 | $comments = $this->app->db->table('demo_comments'); 65 | $comments->insert(array('user_id' => 1, 66 | 'article_id' => 2, 67 | 'comment' => 'Comment for Article 2') 68 | ); 69 | 70 | } 71 | 72 | protected function dropDB() 73 | { 74 | //illuminate/dtabase schema builder 75 | $schema = $this->app->db->getSchemaBuilder(); 76 | 77 | //drop all tables 78 | $schema->dropIfExists("demo_users"); 79 | $schema->dropIfExists("demo_articles"); 80 | $schema->dropIfExists("demo_comments"); 81 | } 82 | 83 | } 84 | -------------------------------------------------------------------------------- /src/Rapyd/Widgets/DataGrid.php: -------------------------------------------------------------------------------- 1 | source = $source; 20 | if (is_null($as) && is_a($source, "\Illuminate\Database\Eloquent\Builder")) { 21 | 22 | $reflection = new \ReflectionClass(get_class($source->getModel())); 23 | $this->row_as = strtolower($reflection->getShortName()); 24 | 25 | } elseif (is_null($as) && is_a($source, "\Illuminate\Database\Eloquent\Model")) { 26 | $reflection = new \ReflectionClass(get_class($source)); 27 | $this->row_as = strtolower($reflection->getShortName()); 28 | } 29 | 30 | return $this; 31 | } 32 | 33 | public function setColumn($name, $label = null, $orderby = false) 34 | { 35 | $config['row_as'] = $this->row_as; 36 | $config['pattern'] = $name; 37 | $config['label'] = ($label != "") ? $label : $name; 38 | $config['orderby'] = $orderby; 39 | 40 | $column = new Column($config); 41 | $this->columns[] = $column; 42 | 43 | return $this; 44 | } 45 | 46 | public function add($name, $label = null, $orderby = false) 47 | { 48 | return $this->setColumn($name, $label, $orderby); 49 | } 50 | protected function buildGrid() 51 | { 52 | $data = get_object_vars($this); 53 | $data['container'] = $this->button_containers(); 54 | 55 | foreach ($this->data as $tablerow) { 56 | unset($row); 57 | foreach ($this->columns as $column) { 58 | 59 | unset($cell); 60 | $column->resetPattern(); 61 | $column->setRow($tablerow); 62 | 63 | $cell = get_object_vars($column); 64 | $cell["value"] = $column->getValue(); 65 | $cell["type"] = $column->column_type; 66 | $row[] = $cell; 67 | } 68 | $this->rows[] = $row; 69 | } 70 | 71 | $view = 'DataGrid.twig'; 72 | $this->app->view()->appendData(array('dg' => $this)); 73 | 74 | return $this->app->view()->render($view); 75 | } 76 | 77 | public function build($type = 'Grid') 78 | { 79 | parent::build(); 80 | //sniff and perform action 81 | //$this->sniff_action(); 82 | foreach ($this->columns as & $column) { 83 | if (isset($column->orderby)) { 84 | 85 | $column->orderby_asc_url = $this->orderby_link($column->orderby, 'asc'); 86 | $column->orderby_desc_url = $this->orderby_link($column->orderby, 'desc'); 87 | } 88 | } 89 | $method = 'build' . $type; 90 | $this->output = $this->$method(); 91 | } 92 | 93 | public function getGrid($type = 'Grid') 94 | { 95 | $this->build($type); 96 | 97 | return $this->output; 98 | } 99 | 100 | } 101 | -------------------------------------------------------------------------------- /src/Modules/Demos/Views/Demo.layout.twig: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | {% block title %} rapyd demo {% endblock %} 6 | 7 | 8 | 9 | 10 | 53 | 54 | 55 | 56 |
57 | 58 |
59 | 64 |

{{ block('title') }}

65 |
66 | 67 | 68 | 77 | 78 |
79 | 80 |
81 | 82 | {% block content %}{% endblock %} 83 | 84 | 85 | 86 |
87 | 88 | 89 | {% block code %} 90 | {#
91 |
 92 |               {{ code|raw }}
 93 |         
94 |
#} 95 | {% endblock code %} 96 | 97 | 98 |
99 | 100 | 103 | 104 |
105 | 106 | 107 | 108 | 109 | 110 | -------------------------------------------------------------------------------- /src/Rapyd/Helpers/Pagination.php: -------------------------------------------------------------------------------- 1 | cid = (isset($config['cid'])) ? $config['cid'] : self::get_identifier(); 32 | if (count($config) > 0) { 33 | $this->initialize($config); 34 | } 35 | } 36 | 37 | protected function getIdentifier() 38 | { 39 | if (self::$identifier < 1) { 40 | self::$identifier++; 41 | 42 | return ""; 43 | } 44 | 45 | return (string) self::$identifier++; 46 | } 47 | 48 | public function initialize($config = array()) 49 | { 50 | foreach ($config as $key => $value) { 51 | if (property_exists($this, $key)) { 52 | $this->$key = $value; 53 | } 54 | } 55 | 56 | if (!isset($this->app)) 57 | $this->app = \Rapyd\Application::getInstance(); 58 | 59 | //unset current pagination 60 | $this->url = $this->app->url->remove('reset' . $this->cid) 61 | ->append('pag' . $this->cid, "-pag-") 62 | ->get() . $this->hash; 63 | 64 | // Core pagination values 65 | $this->total_items = (int) max(0, $this->total_items); 66 | $this->items_per_page = (int) max(1, $this->items_per_page); 67 | $this->total_pages = (int) ceil($this->total_items / $this->items_per_page); 68 | 69 | if (!isset($this->current_page)) 70 | $this->current_page = (int) min(max(1, $this->app->url->value('pag' . $this->cid)), max(1, $this->total_pages)); 71 | $this->current_first_item = (int) min((($this->current_page - 1) * $this->items_per_page) + 1, $this->total_items); 72 | $this->current_last_item = (int) min($this->current_first_item + $this->items_per_page - 1, $this->total_items); 73 | 74 | // If there is no first/last/previous/next page, relative to the 75 | // current page, value is set to FALSE. Valid page number otherwise. 76 | $this->first_page = ($this->current_page == 1) ? false : 1; 77 | 78 | $this->last_page = ($this->current_page >= $this->total_pages) ? false : $this->total_pages; 79 | $this->previous_page = ($this->current_page > 1) ? $this->current_page - 1 : false; 80 | $this->next_page = ($this->current_page < $this->total_pages) ? $this->current_page + 1 : false; 81 | 82 | if ($this->num_links) { 83 | $this->nav_start = (($this->current_page - $this->num_links) > 0) ? $this->current_page - ($this->num_links - 1) : 1; 84 | $this->nav_end = (($this->current_page + $this->num_links) < $this->total_pages) ? $this->current_page + $this->num_links : $this->total_pages; 85 | } else { 86 | $this->nav_start = 1; 87 | $this->nav_end = $this->total_pages; 88 | } 89 | } 90 | 91 | public function links() 92 | { 93 | if ($this->total_pages < 2) 94 | return ''; 95 | 96 | $view = 'Pagination.twig'; 97 | $this->app->view()->appendData(get_object_vars($this)); 98 | $output = $this->app->view()->render($view); 99 | 100 | return $output; 101 | } 102 | 103 | public function __toString() 104 | { 105 | return $this->links(); 106 | } 107 | 108 | public function offset() 109 | { 110 | return (int) ($this->current_page - 1) * $this->items_per_page; 111 | } 112 | 113 | } 114 | -------------------------------------------------------------------------------- /src/Rapyd/Helpers/HTML.php: -------------------------------------------------------------------------------- 1 | $value) { 20 | if (is_int($attribute)) { 21 | $attribute = $value; 22 | } 23 | $attr .= ' ' . $attribute . '="' . $value . '"'; 24 | } 25 | } 26 | 27 | return $attr; 28 | } 29 | 30 | /** 31 | * Creates a HTML tag. 32 | * 33 | * @param string $name Tag name 34 | * @param array $attributes (optional) Tag attributes 35 | * @param string $content (optional) Tag content 36 | * @return string 37 | */ 38 | public static function tag($name, array $attributes = array(), $content = null) 39 | { 40 | return '<' . $name . static::attributes($attributes) . (($content === null) ? ' />' : '>' . $content . ''); 41 | } 42 | 43 | /** 44 | * Open a HTML tag 45 | * 46 | * @param string $name 47 | * @param array $attributes 48 | * @return string 49 | */ 50 | public static function open($name, array $attributes = array()) 51 | { 52 | return '<' . $name . static::attributes($attributes) . ' />'; 53 | } 54 | 55 | /** 56 | * Close a HTML tag 57 | * 58 | * @param string $name 59 | * @param array $attributes 60 | * @return string 61 | */ 62 | public static function close($name) 63 | { 64 | return ''; 65 | } 66 | 67 | /** 68 | * Helper method for building media tags. 69 | * 70 | * @param string $type Tag type 71 | * @param mixed $files File or array of files 72 | * @param array $attributes (optional) Tag attributes 73 | */ 74 | protected static function buildMedia($type, $files, $attributes) 75 | { 76 | $sources = ''; 77 | 78 | foreach ((array) $files as $file) { 79 | $sources .= HTML::tag('source', array('src' => $file)); 80 | } 81 | 82 | return static::tag($type, $attributes, $sources); 83 | } 84 | 85 | /** 86 | * Creates audio tag with support for multiple sources. 87 | * 88 | * @param mixed $files File or array of files 89 | * @param array $attributes (optional) Tag attributes 90 | */ 91 | public static function audio($files, array $attributes = array()) 92 | { 93 | return static::buildMedia('audio', $files, $attributes); 94 | } 95 | 96 | /** 97 | * Creates video tag with support for multiple sources. 98 | * 99 | * @param mixed $files File or array of files 100 | * @param array $attributes (optional) Tag attributes 101 | */ 102 | public static function video($files, array $attributes = array()) 103 | { 104 | return static::buildMedia('video', $files, $attributes); 105 | } 106 | 107 | /** 108 | * Helper method for building list tags. 109 | * 110 | * @param string $type Tag type 111 | * @param mixed $items File or array of files 112 | * @param array $attributes (optional) Tag attributes 113 | */ 114 | protected static function buildList($type, $items, $attributes) 115 | { 116 | $list = ''; 117 | 118 | foreach ($items as $item) { 119 | if (is_array($item)) { 120 | $list .= static::tag('li', array(), static::buildList($type, $item, array())); 121 | } else { 122 | $list .= static::tag('li', array(), $item); 123 | } 124 | } 125 | 126 | return static::tag($type, $attributes, $list); 127 | } 128 | 129 | /** 130 | * Builds an un-ordered list. 131 | * 132 | * @param array $items List items 133 | * @param array $attributes List attributes 134 | * @return string 135 | */ 136 | public static function ul(array $items, array $attributes = array()) 137 | { 138 | return static::buildList('ul', $items, $attributes); 139 | } 140 | 141 | /** 142 | * Builds am ordered list. 143 | * 144 | * @param array $items List items 145 | * @param array $attributes List attributes 146 | * @return string 147 | */ 148 | public static function ol(array $items, array $attributes = array()) 149 | { 150 | return static::buildList('ol', $items, $attributes); 151 | } 152 | 153 | } 154 | -------------------------------------------------------------------------------- /src/Rapyd/Widgets/DataGrid/Column.php: -------------------------------------------------------------------------------- 1 | check_pattern(); 37 | } 38 | 39 | protected function check_pattern() 40 | { 41 | if (is_object($this->pattern)) { 42 | $this->pattern_type = "field_object"; 43 | $this->field = $this->pattern; 44 | if ($this->orderby === true) { 45 | $this->orderby_field = $this->field->name; 46 | } 47 | } else { 48 | $this->field_list = $this->parser->variables($this->pattern); 49 | if (is_array($this->field_list)) { 50 | $this->pattern_type = "pattern"; 51 | if ($this->orderby === true) { 52 | $this->orderby_field = $this->field_list[0]; 53 | } 54 | } else { 55 | $this->pattern_type = "field_name"; 56 | $this->field_name = $this->pattern; 57 | if ($this->orderby === true) { 58 | $this->orderby_field = (isset($this->orderby_field)) ? $this->orderby_field : $this->field_name; 59 | } 60 | } 61 | } 62 | if ($this->orderby) { 63 | $this->column_type = 'orderby'; 64 | } 65 | } 66 | 67 | protected function resetPattern() 68 | { 69 | $this->rpattern = $this->pattern; 70 | } 71 | 72 | protected function setPattern($pattern) 73 | { 74 | //$this->parser->render($this->pattern, $data_row); 75 | $this->pattern = $pattern; 76 | } 77 | 78 | protected function setLabel($label) 79 | { 80 | $this->label = $label; 81 | } 82 | 83 | public function setOrderby($orderby) 84 | { 85 | $this->orderby = $orderby; 86 | //if ($orderby === true) { 87 | // $this->orderby = (isset($this->orderby_field)) ? $this->orderby_field : $this->field_name; 88 | //} 89 | return $this; 90 | } 91 | 92 | protected function setUrl($url, $img = '', $onclick = '') 93 | { 94 | $this->url = $url; 95 | $this->img = $img; 96 | $this->onclick = $onclick; 97 | 98 | return $this; 99 | } 100 | 101 | protected function setRow_as($row_as) 102 | { 103 | $this->row_as = $row_as; 104 | } 105 | 106 | // -------------------------------------------------------------------- 107 | public function setAttributes($attributes) 108 | { 109 | $this->attributes = $attributes; 110 | 111 | return $this; 112 | } 113 | 114 | // -------------------------------------------------------------------- 115 | public function setCallback($callback, $object = null) 116 | { 117 | $this->callback = $callback; 118 | $this->callback_object = $object; 119 | } 120 | 121 | // -------------------------------------------------------------------- 122 | public function setTRAttributes($attributes) 123 | { 124 | $this->tr_attributes = $attributes; 125 | 126 | return $this; 127 | } 128 | 129 | // -------------------------------------------------------------------- 130 | public function setRow($data_row) 131 | { 132 | if (isset($data_row[$this->pattern])) { 133 | $this->rpattern = $data_row[$this->pattern]; 134 | } else { 135 | if (isset($this->row_as)) { 136 | $data_row = array($this->row_as => $data_row); 137 | } else { 138 | $data_row = get_object_vars($data_row); 139 | } 140 | $this->rpattern = $this->parser->render($this->pattern, $data_row); 141 | } 142 | 143 | if (isset($this->callback_object)) { 144 | $this->rpattern = call_user_func(array($this->callback_object, $this->callback), $data_row); 145 | } elseif (isset($this->callback)) { 146 | $this->rpattern = call_user_func($this->callback, $data_row); 147 | } 148 | if ($this->url) { 149 | if (!isset($this->attributes['style'])) 150 | $this->attributes['style'] = 'width: 70px; text-align:center; padding-right:5px'; 151 | $this->link = $this->parser->render($this->url, $data_row); 152 | } 153 | 154 | //manage attributes 155 | } 156 | 157 | /** 158 | * a column value by default is a string: the field-name you want to show in the column 159 | * but it support also a "pattern" with html and placeholders like : {field1}
{field2} 160 | * @return type 161 | */ 162 | public function getValue() 163 | { 164 | 165 | if ($this->rpattern == "") { 166 | $this->rpattern = " "; 167 | } 168 | 169 | return $this->rpattern; 170 | } 171 | 172 | /** 173 | * replace {field} with value 174 | * @return string 175 | */ 176 | public function orderby_link() 177 | { 178 | die('e mo?'); 179 | 180 | return str_replace('{field}', $column->orderby_field, $this->orderby_asc_url); 181 | } 182 | 183 | } 184 | -------------------------------------------------------------------------------- /src/Rapyd/Widgets/DataSet.php: -------------------------------------------------------------------------------- 1 | source) and is_object($this->source)) { 46 | $this->cid = $this->source->cid; 47 | } 48 | //or generate new one 49 | else { 50 | $this->cid = parent::getIdentifier(); 51 | } 52 | $this->app = \Rapyd\Application::getInstance(); 53 | } 54 | 55 | // -------------------------------------------------------------------- 56 | 57 | public function count() 58 | { 59 | return count($this->data); 60 | } 61 | 62 | public function setSource($source) 63 | { 64 | $this->source = $source; 65 | 66 | return $this; 67 | } 68 | 69 | public function table($table) 70 | { 71 | $this->query = $this->app->db->table($table); 72 | 73 | return $this->query; 74 | } 75 | 76 | public function setPagination($per_page, $num_links=3) 77 | { 78 | $this->per_page = $per_page; 79 | $this->num_links= $num_links; 80 | 81 | return $this; 82 | } 83 | 84 | public function orderby_link($field, $dir="asc") 85 | { 86 | $url = ($dir == "asc") ? $this->orderby_uri_asc : $this->orderby_uri_desc ; 87 | 88 | return str_replace('-field-', $field, $url); 89 | 90 | } 91 | 92 | // -------------------------------------------------------------------- 93 | 94 | public function orderby($field, $direction) 95 | { 96 | $this->orderby = array($field, $direction); 97 | } 98 | 99 | // -------------------------------------------------------------------- 100 | 101 | protected function limit($limit, $offset) 102 | { 103 | $this->limit = array($limit, $offset); 104 | } 105 | 106 | // -------------------------------------------------------------------- 107 | 108 | public function build() 109 | { 110 | if (is_string($this->source) && strpos(" ", $this->source) === false) { 111 | //tablename 112 | $this->type = "query"; 113 | $this->query = $this->table($this->source); 114 | $this->total_rows = $this->query->count(); 115 | } elseif (is_a($this->source, "\Illuminate\Database\Eloquent\Model") || is_a($this->source,"\Illuminate\Database\Eloquent\Builder")) { 116 | $this->type = "model"; 117 | $this->query = $this->source; 118 | $this->total_rows = $this->query->count(); 119 | } 120 | //array 121 | elseif (is_array($this->source)) { 122 | $this->type = "array"; 123 | $this->total_rows = count($this->source); 124 | } 125 | //exception 126 | 127 | //offset and pagination setup/detect 128 | $config = array( 129 | 'cid' => $this->cid, 130 | 'total_items' => $this->total_rows, // use db count query here of course 131 | 'items_per_page' => $this->per_page, // it may be handy to set defaults for stuff like this in config/pagination.php 132 | 'num_links' => round($this->num_links/2), 133 | 'hash' => $this->hash, 134 | 'url' => $this->url, 135 | 'current_page' => $this->current_page, 136 | ); 137 | $this->pagination = new \Rapyd\Helpers\Pagination($config); 138 | $offset = $this->pagination->offset(); 139 | 140 | $this->limit($this->per_page, $offset); 141 | 142 | //build orderby urls 143 | $this->orderby_uri_asc = $this->app->url->remove('pag' . $this->cid)->remove('reset' . $this->cid)->append('orderby' . $this->cid, array("-field-", "asc"))->get() . $this->hash; 144 | $this->orderby_uri_desc = $this->app->url->remove('pag' . $this->cid)->remove('reset' . $this->cid)->append('orderby' . $this->cid, array("-field-", "desc"))->get() . $this->hash; 145 | 146 | //detect orderby 147 | $orderby = $this->app->url->value("orderby" . $this->cid); 148 | if ($orderby) { 149 | $this->orderby_field = $orderby[0]; 150 | $this->orderby_direction = $orderby[1]; 151 | $this->orderby($this->orderby_field, $this->orderby_direction); 152 | } 153 | 154 | //build subset of data 155 | switch ($this->type) { 156 | case "array": 157 | //orderby 158 | if (isset($this->orderby)) { 159 | list($field, $direction) = $this->orderby; 160 | $column = array(); 161 | foreach ($this->source as $key => $row) { 162 | $column[$key] = $row[$field]; 163 | } 164 | if ($direction == "asc") { 165 | array_multisort($column, SORT_ASC, $this->source); 166 | } else { 167 | array_multisort($column, SORT_DESC, $this->source); 168 | } 169 | } 170 | 171 | //limit-offset 172 | if (isset($this->limit)) { 173 | $this->source = array_slice($this->source, $this->limit[1], $this->limit[0]); 174 | } 175 | $this->data = $this->source; 176 | break; 177 | 178 | case "query": 179 | case "model": 180 | //orderby 181 | 182 | if (isset($this->orderby)) { 183 | $this->query = $this->query->orderBy($this->orderby[0], $this->orderby[1]); 184 | } 185 | //limit-offset 186 | if (isset($this->limit)) { 187 | $this->query = $this->query->skip($this->pagination->offset())->take($this->per_page); 188 | } 189 | 190 | $this->data = $this->query->get(); 191 | break; 192 | } 193 | 194 | return $this; 195 | } 196 | 197 | public function getSet() 198 | { 199 | $this->build(); 200 | 201 | return $this; 202 | } 203 | 204 | public function getData() 205 | { 206 | return $this->data; 207 | } 208 | public function links() 209 | { 210 | return $this->pagination->links(); 211 | } 212 | 213 | } 214 | 215 | // End Dataset Class 216 | -------------------------------------------------------------------------------- /src/Rapyd/Controller.php: -------------------------------------------------------------------------------- 1 | app = $app; 62 | 63 | $this->db = $app->db; 64 | $this->form = $app->form; 65 | $this->grid = $app->grid; 66 | $this->set = $app->set; 67 | 68 | if ($renderTemplateSuffix = $app->config('controller.template_suffix')) { 69 | $this->renderTemplateSuffix = $renderTemplateSuffix; 70 | } 71 | if (!is_null($paramPrefix = $app->config('controller.param_prefix'))) { 72 | $this->paramPrefix = $paramPrefix; 73 | } 74 | $this->renderTemplateSuffix = $app->config('controller.template_suffix'); 75 | } 76 | 77 | /** 78 | * Renders output with given template 79 | * 80 | * @param string $template Name of the template to be rendererd 81 | * @param array $args Args for view 82 | */ 83 | protected function render($template, $args = null) 84 | { 85 | 86 | //check if controller is in a module, in this case.. 87 | if (!is_null($args)) { 88 | $this->app->view()->appendData($args); 89 | } 90 | if (!is_null($this->renderTemplateSuffix) 91 | && !preg_match('/\.'. $this->renderTemplateSuffix. '$/', $template) 92 | ) { 93 | $template .= '.'. $this->renderTemplateSuffix; 94 | } 95 | echo $this->app->view()->render($template); 96 | } 97 | 98 | protected function fetch($template, $args = null) 99 | { 100 | if (!is_null($args)) { 101 | $this->app->view()->appendData($args); 102 | } 103 | if (!is_null($this->renderTemplateSuffix) 104 | && !preg_match('/\.'. $this->renderTemplateSuffix. '$/', $template) 105 | ) { 106 | $template .= '.'. $this->renderTemplateSuffix; 107 | } 108 | 109 | return $this->app->view()->fetch($template); 110 | } 111 | 112 | /** 113 | * Performs redirect 114 | * 115 | * @param string $path 116 | */ 117 | protected function redirect($path) 118 | { 119 | return $this->app->redirect($path); 120 | } 121 | 122 | /** 123 | * Slim's request object 124 | * 125 | * @return \Slim\Request 126 | */ 127 | protected function request() 128 | { 129 | return $this->app->request(); 130 | } 131 | 132 | 133 | /** 134 | * Returns a single parameter of the "data[Object][Key]" format. 135 | * 136 | * 137 | $paramValue = $this->param('prefix.name'); // prefix[name] -> "string value" 138 | $paramValue = $this->param('prefix.name', 'post'); // prefix[name] -> "string value" 139 | $paramValue = $this->param('prefix.name', 'get'); // prefix[name] -> "string value" 140 | * 141 | * 142 | * @param mixed $name Name of the parameter 143 | * @param mixed $reqMode Optional mode. Either null (all params), true | "post" 144 | * (only POST params), false | "get" (only GET params) 145 | * 146 | * @return mixed Either array or single string or null 147 | */ 148 | protected function param($name, $reqMode = null) 149 | { 150 | $args = array(); 151 | if (is_array($name)) { 152 | 153 | // ["name"] 154 | if (count($name) === 1) { 155 | $name = $name[0]; 156 | } 157 | 158 | // ["name", ["constraint" => "..", ..]] 159 | elseif (is_array($name[1])) { 160 | list($name, $args) = $name; 161 | } 162 | 163 | // ["name", "constraint" => "..", ..] 164 | else { 165 | $n = array_shift($name); 166 | $args = $name; 167 | $name = $n; 168 | } 169 | } 170 | $args = array_merge(array( 171 | 'constraint' => null, 172 | 'default' => null, 173 | 'raw' => false 174 | ), $args); 175 | 176 | // prefix name 177 | $name = $this->paramPrefix. $name; 178 | 179 | // determine method for request 180 | $reqMeth = $this->paramAccessorMeth($reqMode); 181 | 182 | // determine stash name 183 | $reqStashName = 'params'. ucfirst($reqMeth); 184 | if (is_null($this->$reqStashName)) { 185 | $this->$reqStashName = $this->request()->$reqMeth(); 186 | } 187 | $params = $this->$reqStashName; 188 | 189 | // split of parts and go through 190 | $parts = preg_split('/\./', $name); 191 | while (isset($params[$parts[0]])) { 192 | $params = $params[$parts[0]]; 193 | array_shift($parts); 194 | if (empty($parts)) { 195 | return $this->cleanupParam($params, $args); 196 | } 197 | } 198 | 199 | return null; 200 | } 201 | 202 | private function cleanupParam($value, $args) 203 | { 204 | if (is_array($value)) { 205 | foreach ($value as $k => $v) { 206 | $clean = $this->cleanupParam($v, $args); 207 | if (!is_null($clean)) { 208 | $value[$k] = $clean; 209 | } 210 | } 211 | 212 | return $value; 213 | } else { 214 | 215 | // cleanup 216 | if ($this->paramCleanup && !$args['raw']) { 217 | $value = preg_replace('/>/', '', 218 | preg_replace('/ 245 | $params = $this->params(['prefix.name', 'other.name']); // -> ["prefix.name" => "value", ..] 246 | $params = $this->params(['prefix.name', 'other.name'], true); // -> null if not all found 247 | $params = $this->params(['prefix.name', 'other.name'], ['other.name' => "Default Value"]); 248 | * 249 | * 250 | * @param mixed $name Name or names of parameters (GET or POST) 251 | * @param mixed $reqMode Optional mode. Either null (all params), true | "post" 252 | * (only POST params), false | "get" (only GET params) 253 | * @param mixed $defaults Either true (require ALL given or return null), array (defaults) 254 | * 255 | * @return mixed Either array or single string or null 256 | */ 257 | protected function params($names = array(), $reqMode = null, $defaults = null) 258 | { 259 | // no names given -> get them all 260 | if (!$names) { 261 | $reqMeth = $this->paramAccessorMeth($reqMode); 262 | $params = $this->request()->$reqMeth(); 263 | $namesPre = self::flatten($params); 264 | $names = array_keys($namesPre); 265 | if ($prefix = $this->paramPrefix) { 266 | $prefixLen = strlen($prefix); 267 | $names = array_map(function ($key) use ($prefixLen) { 268 | return substr($key, $prefixLen); 269 | }, array_filter($names, function ($in) use ($prefix) { 270 | return strpos($in, $prefix) === 0; 271 | })); 272 | } 273 | } 274 | $res = array(); 275 | foreach ($names as $n) { 276 | $param = $this->param($n, $reqMode); 277 | if (!is_null($param) && (!is_array($param) || !empty($param))) { 278 | $res[$n] = $param; 279 | } 280 | 281 | // if in "need all" mode 282 | elseif ($defaults === true) { 283 | return null; 284 | } 285 | 286 | // if in default mode 287 | elseif (is_array($defaults) && isset($defaults[$n])) { 288 | $res[$n] = $defaults[$n]; 289 | } 290 | } 291 | 292 | return $res; 293 | } 294 | 295 | 296 | protected function paramAccessorMeth($reqMode = null) 297 | { 298 | return $reqMode === true || $reqMode === 'post' // POST 299 | ? 'post' 300 | : ($reqMode === false || $reqMode === 'get' // GET 301 | ? 'get' 302 | : 'params' // ALL 303 | ); 304 | } 305 | 306 | 307 | 308 | protected static function flatten($data, $flat = array(), $prefix = '') 309 | { 310 | foreach ($data as $key => $value) { 311 | 312 | // is array -> flatten deeped 313 | if (is_array($value)) { 314 | $flat = self::flatten($value, $flat, $prefix. $key. '.'); 315 | } 316 | // scalar -> use 317 | else { 318 | $flat[$prefix. $key] = $value; 319 | } 320 | } 321 | 322 | return $flat; 323 | } 324 | 325 | } 326 | -------------------------------------------------------------------------------- /src/Rapyd/Helpers/Url.php: -------------------------------------------------------------------------------- 1 | 1, 20 | 'reset'=>1, 21 | 'pag'=>1, 22 | 'orderby'=>2, 23 | 'show'=>1, 24 | 'create'=>1, 25 | 'modify'=>1, 26 | 'delete'=>1, 27 | 'insert'=>1, 28 | 'update'=>1, 29 | 'do_delete'=>1, 30 | 'process'=>1); 31 | 32 | public function __construct($app) 33 | { 34 | $this->app = $app; 35 | 36 | return $this; 37 | } 38 | 39 | public static function unparse_str($array) 40 | { 41 | return '?' . preg_replace('/%5B[0-9]+%5D/simU', '[]', http_build_query($array)); 42 | } 43 | 44 | public function set($url) 45 | { 46 | $this->url = $url; 47 | 48 | return $this; 49 | } 50 | 51 | public function get() 52 | { 53 | if ($this->url == '') { 54 | return $this->current(); 55 | } else { 56 | $url = $this->url; 57 | $this->url = ''; 58 | 59 | return $url; 60 | } 61 | } 62 | 63 | public function current() 64 | { 65 | //var_dump($this->app->router()->getCurrentRoute()->getPattern()); 66 | //die; 67 | 68 | if (isset($_SERVER['HTTP_X_ORIGINAL_URL'])) 69 | $_SERVER['REQUEST_URI'] = $_SERVER['HTTP_X_ORIGINAL_URL']; 70 | $url = (isset($_SERVER['REQUEST_URI'])) ? $_SERVER['REQUEST_URI'] : @getenv('REQUEST_URI'); 71 | 72 | return $url; 73 | } 74 | 75 | public function append($key, $value) 76 | { 77 | return ($this->from == 'uri') ? $this->appendUri($key, $value) : $this->appendQS($key, $value); 78 | } 79 | 80 | public function remove($key, $params = 1) 81 | { 82 | return ($this->from == 'uri') ? $this->removeUri($key, $params) : $this->removeQS($key); 83 | } 84 | 85 | public function removeAll($key) 86 | { 87 | return ($this->from == 'uri') ? $this->removeAllUri($key) : $this->removeAllQS($key); 88 | } 89 | 90 | public function replace($key, $newkey) 91 | { 92 | return ($this->from == 'uri') ? $this->replaceUri($key, $newkey) : $this->replaceQS($key, $newkey); 93 | } 94 | 95 | public function value($key, $default = false) 96 | { 97 | return ($this->from == 'uri') ? $this->valueUri($key, $default) : $this->valueQS($key, $default); 98 | } 99 | 100 | public function appendQS($key, $value) 101 | { 102 | $url = $this->get(); 103 | $qs_array = array(); 104 | if (strpos($url, '?') !== false) { 105 | $qs = substr($url, strpos($url, '?') + 1); 106 | $url = substr($url, 0, strpos($url, '?')); 107 | parse_str($qs, $qs_array); 108 | } 109 | $qs_array[$key] = $value; 110 | $query_string = self::unparse_str($qs_array); 111 | $this->url = $url . $query_string; 112 | 113 | return $this; 114 | } 115 | 116 | public function removeQS($keys) 117 | { 118 | $qs_array = array(); 119 | $url = $this->get(); 120 | if (strpos($url, '?') === false) { 121 | $this->url = $url; 122 | 123 | return $this; 124 | } 125 | $qs = substr($url, strpos($url, '?') + 1); 126 | $url = substr($url, 0, strpos($url, '?')); 127 | parse_str($qs, $qs_array); 128 | 129 | if (!is_array($keys)) { 130 | if ($keys == 'ALL') { 131 | $this->url = $url; 132 | 133 | return $this; 134 | } 135 | $keys = array($keys); 136 | } 137 | foreach ($keys as $key) { 138 | unset($qs_array[$key]); 139 | } 140 | $query_string = self::unparse_str($qs_array); 141 | 142 | $this->url = $url . $query_string; 143 | 144 | return $this; 145 | } 146 | 147 | public function removeAllQS($cid = null) 148 | { 149 | $semantic = array_keys($this->semantic); 150 | if (isset($cid)) { 151 | 152 | foreach ($semantic as $key) { 153 | $keys[] = $key . $cid; 154 | } 155 | $semantic = $keys; 156 | } 157 | 158 | return $this->remove($semantic); 159 | } 160 | 161 | public function replaceQS($key, $newkey) 162 | { 163 | $qs_array = array(); 164 | $url = $this->get(); 165 | if (strpos($url, '?') !== false) { 166 | $qs = substr($url, strpos($url, '?') + 1); 167 | $url = substr($url, 0, strpos($url, '?')); 168 | parse_str($qs, $qs_array); 169 | } 170 | if (isset($qs_array[$key])) { 171 | $qs_array[$newkey] = $qs_array[$key]; 172 | unset($qs_array[$key]); 173 | } 174 | $query_string = self::unparse_str($qs_array); 175 | $this->url = $url . $query_string; 176 | 177 | return $this; 178 | } 179 | 180 | public function valueQS($key, $default = false) 181 | { 182 | if (strpos($key, '|')) { 183 | $keys = explode('|', $key); 184 | foreach ($keys as $k) { 185 | $v = $this->valueQS($k, $default); 186 | if ($v != $default) 187 | return $v; 188 | } 189 | 190 | return $default; 191 | } 192 | 193 | parse_str(parse_url($this->current(), PHP_URL_QUERY), $params); 194 | if (strpos($key, '.')) { 195 | list($namespace, $subkey) = explode('.', $key); 196 | 197 | return (isset($params[$namespace][$key])) ? $params[$namespace][$key] : $default; 198 | } else { 199 | return (isset($params[$key])) ? $params[$key] : $default; 200 | } 201 | } 202 | 203 | //------------------------------- 204 | 205 | public function appendUri($key, $value = null) 206 | { 207 | 208 | $url = $this->get(); 209 | $seg_array = explode("/", trim($url, '/')); 210 | 211 | $key_position = array_search($key, $seg_array); 212 | if ($key_position !== false) { 213 | 214 | array_splice($seg_array, $key_position, count((array) $value) + 1, array_merge((array) $key, array_map('strval', (array) $value))); 215 | } else { 216 | $seg_array = array_merge($seg_array, array_merge((array) $key, array_map('strval', (array) $value))); 217 | } 218 | 219 | $this->url = "/".implode('/', $seg_array); 220 | 221 | return $this; 222 | 223 | } 224 | 225 | public function removeUri($keys, $params = 1) 226 | { 227 | $url = $this->get(); 228 | $seg_array = explode("/", trim($url, '/')); 229 | 230 | if (!is_array($keys)) { 231 | $keys = array($keys); 232 | } 233 | 234 | foreach ($keys as $key) { 235 | $key_position = array_search($key, $seg_array); 236 | if ($key_position !== false) { 237 | 238 | $kkey = preg_replace('@\d*$@', '', $key); 239 | if (isset($this->semantic[$kkey])) { 240 | array_splice($seg_array, $key_position, $this->semantic[$kkey] + 1); 241 | } else { 242 | array_splice($seg_array, $key_position, $params + 1); 243 | } 244 | } 245 | } 246 | 247 | $this->url = "/".implode('/', $seg_array); 248 | 249 | return $this; 250 | } 251 | 252 | public function removeAllUri($cid = null) 253 | { 254 | $url = $this->get(); 255 | $keys = array(); 256 | if (isset($cid)) { 257 | foreach ($this->semantic as $key => $params) { 258 | $keys[] = $key . $cid; 259 | } 260 | $this->url = $this->removeUri($keys)->get(); 261 | 262 | } else { 263 | 264 | $uri = $this->get(); 265 | $seg_array = explode("/", trim($uri, '/')); 266 | foreach ($this->semantic as $key => $params) { 267 | if (preg_match_all('@(' . $key . '\d*)@', $url, $matches)) 268 | foreach ($matches[1] as $mkey) { 269 | $this->url = $this->removeUri($mkey, $this->semantic[$key] + 1)->get(); 270 | } 271 | } 272 | } 273 | 274 | return $this; 275 | } 276 | 277 | public function replaceUri($key, $newkey, $url = null) 278 | { 279 | $url = $this->get(); 280 | $seg_array = explode("/", trim($url, '/')); 281 | 282 | $key_position = array_search($key, $seg_array); 283 | if ($key_position !== false) { 284 | array_splice($seg_array, $key_position, 1, array($newkey)); 285 | } 286 | $this->url = "/".implode('/', $seg_array); 287 | 288 | return $this; 289 | 290 | } 291 | 292 | public function valueUri($key, $default = false, $params = 1) 293 | { 294 | 295 | $url = $this->get(); 296 | $seg_array = explode("/", trim($url, '/')); 297 | 298 | if (strpos($key, '|')) { 299 | $keys = explode('|', $key); 300 | foreach ($keys as $k) { 301 | $v = $this->valueUri($k, $default); 302 | if ($v != $default) 303 | return $v; 304 | } 305 | 306 | return $default; 307 | } 308 | 309 | if (strpos($key, '.')) { 310 | //... 311 | } else { 312 | $key_position = array_search($key, $seg_array); 313 | if ($key_position !== false) { 314 | if (isset($seg_array[$key_position + 1])) { 315 | //.. 316 | $skey = preg_replace("@([a-z]+)\d@", "\\1", $key); 317 | 318 | if (isset($this->semantic[$skey])) { 319 | if ($this->semantic[$skey] == 0) 320 | return true; 321 | elseif ($this->semantic[$skey] < 2) 322 | return $seg_array[$key_position + 1]; 323 | else 324 | return array_slice($seg_array, $key_position + 1, $this->semantic[$skey]); 325 | } else { 326 | if ($params == 0) 327 | return true; 328 | elseif ($params < 2) 329 | return $seg_array[$key_position + 1]; 330 | else 331 | return array_slice($seg_array, $key_position + 1, $params); 332 | } 333 | } else { 334 | return true; 335 | } 336 | } 337 | } 338 | 339 | return $default; 340 | } 341 | 342 | } 343 | -------------------------------------------------------------------------------- /src/Rapyd/Application.php: -------------------------------------------------------------------------------- 1 | setupDatabase(); 84 | $this->setupView(); 85 | $this->setupForms(); 86 | $this->setupWidgets(); 87 | //custom call, nothing to setup 88 | } else { 89 | 90 | parent::__construct($config); 91 | } 92 | 93 | $this->url = new \Rapyd\Helpers\Url($this); 94 | $this->url->from = $config['url_method']; 95 | } 96 | 97 | public function setupRoute($routes = array()) 98 | { 99 | //widgets route conditions 100 | include __DIR__ . '/Config/routes.php'; 101 | 102 | require __DIR__.'/../src/App/Config/hooks.php'; 103 | require __DIR__.'/../src/Rapyd/Config/routes.php'; 104 | require __DIR__.'/../src/App/Config/routes.php'; 105 | require __DIR__.'/../src/Modules/Demos/Config/routes.php'; 106 | 107 | //add default routes 108 | if (empty($routes)) { 109 | $routes = include __DIR__ . '/../App/Config/routes.php'; 110 | 111 | $module_dir = __DIR__ . '/../Modules/'; 112 | if (file_exists($module_dir)) { 113 | $modules = array_diff(scandir($module_dir), array('..', '.')); 114 | foreach ($modules as $module) { 115 | if (file_exists($module_dir . $module . '/Config/routes.php')) { 116 | $module_routes = include $module_dir . $module . '/Config/routes.php'; 117 | $this->addRoutes($module_routes); 118 | } 119 | } 120 | } 121 | } 122 | $this->addRoutes($routes); 123 | } 124 | 125 | public function setupDatabase($db = array()) 126 | { 127 | //add default routes 128 | if (empty($db)) 129 | $db = include __DIR__ . '/../App/Config/db.php'; 130 | 131 | $capsule = new Capsule(); 132 | $capsule->addConnection($db, 'default'); 133 | $capsule->container['config']['database.fetch'] = \PDO::FETCH_CLASS; 134 | $capsule->bootEloquent(); 135 | 136 | // setup db 137 | $this->db = $capsule->getConnection('default'); 138 | } 139 | 140 | public function setupView(array $twig = array()) 141 | { 142 | //add default routes 143 | if (empty($twig)) 144 | $twig = include __DIR__ . '/../App/Config/twig.php'; 145 | 146 | // Prepare view to use twig 147 | $this->view(new \Slim\Views\Twig()); 148 | $this->view->parserOptions = $twig; 149 | 150 | $views_arr = array(VIEWS_DIR, VENDOR_TWIG_BRIDGE_DIR . '/Resources/views/Form'); 151 | 152 | $module_dir = dirname(__DIR__) . '/Modules/'; 153 | if (file_exists($module_dir)) { 154 | $modules = array_diff(scandir($module_dir), array('..', '.')); 155 | foreach ($modules as $module) { 156 | if (file_exists($module_dir . $module . '/Views')) { 157 | $views_arr[] = $module_dir . $module . '/Views'; 158 | } 159 | } 160 | } 161 | $views_arr[] = __DIR__ . '/Views'; 162 | $this->view->twigTemplateDirs = $views_arr; 163 | 164 | $this->view->parserExtensions = array( 165 | new Twig_Extension_Debug() 166 | ); 167 | 168 | //to move somewhere 169 | $function = new Twig_SimpleFunction('active_class', function ($path, $class = " class=\"active\"") { 170 | return (preg_match("#{$path}#", $_SERVER["REQUEST_URI"])) ? $class : ''; 171 | }, array('is_safe' => array('html'))); 172 | $this->view->getInstance()->addFunction($function); 173 | 174 | $function = new Twig_SimpleFunction('source_code', function ($filepath) { 175 | $code = file_get_contents(VIEWS_DIR . $filepath); 176 | $code = preg_replace("#{% block code %}.*{% endblock %}#Us", '', $code); 177 | $code = highlight_string($code, true); 178 | 179 | return "
\n" . $code . "\n
"; 180 | }, array('is_safe' => array('html'))); 181 | $this->view->getInstance()->addFunction($function); 182 | } 183 | 184 | protected function setupForms() 185 | { 186 | 187 | // Set up the CSRF provider 188 | $csrfProvider = new DefaultCsrfProvider(CSRF_SECRET); 189 | 190 | // Set up the Translation component 191 | $translator = new Translator('en'); 192 | $translator->setFallbackLocale(array('en')); 193 | 194 | $translator->setLocale('en'); 195 | $translator->addLoader('xlf', new XliffFileLoader()); 196 | $translator->addResource('xlf', VENDOR_FORM_DIR . '/Resources/translations/validators.en.xlf', 'en', 'validators'); 197 | $translator->addResource('xlf', VENDOR_VALIDATOR_DIR . '/Resources/translations/validators.en.xlf', 'en', 'validators'); 198 | 199 | // Set up the Validator component 200 | $validator = Validation::createValidatorBuilder() 201 | ->setTranslator($translator) 202 | ->setTranslationDomain('validators') 203 | ->getValidator(); 204 | 205 | $formEngine = new TwigRendererEngine(array(DEFAULT_FORM_THEME)); 206 | 207 | $twig = $this->view->getInstance(); 208 | $formEngine->setEnvironment($twig); 209 | $twig->addExtension(new TranslationExtension($translator)); 210 | $twig->addExtension(new FormExtension(new TwigRenderer($formEngine, $csrfProvider))); 211 | 212 | // Set up the Form component 213 | $this->form = Forms::createFormFactoryBuilder() 214 | ->addExtension(new CsrfExtension($csrfProvider)) 215 | ->addExtension(new ValidatorExtension($validator)) 216 | ->getFormFactory(); 217 | } 218 | 219 | protected function setupWidgets() 220 | { 221 | $this->grid = new \Rapyd\Widgets\WidgetBuilder("\\Rapyd\\Widgets\\DataGrid"); //new \Rapyd\Helpers\Url;//new WidgetBuilder('DataGrid'); 222 | $this->set = new \Rapyd\Widgets\WidgetBuilder("\\Rapyd\\Widgets\\DataSet"); 223 | 224 | } 225 | 226 | public function addRoutes(array $routings, $condition = null) 227 | { 228 | foreach ($routings as $path => $args) { 229 | $httpMethod = 'any'; 230 | 231 | // simple 232 | if (!is_array($args)) { 233 | $args = array($args); 234 | } 235 | 236 | // specific HTTP method 237 | if (count($args) > 1 && is_string($args[1])) { 238 | $classAction = array_shift($args); 239 | $httpMethod = array_shift($args); 240 | array_unshift($args, $classAction); 241 | } 242 | 243 | // readd path & extract route 244 | array_unshift($args, $path); 245 | $this->extractControllerFromRoute($args, $condition); 246 | 247 | // call "map" method to add routing 248 | $route = call_user_func_array(array($this, 'map'), $args); 249 | if ('any' === $httpMethod) { 250 | $route->via('GET', 'POST'); 251 | } else { 252 | $route->via(strtoupper($httpMethod)); 253 | } 254 | if ($condition) { 255 | $route->conditions($condition); 256 | } 257 | } 258 | 259 | return $this; 260 | } 261 | 262 | protected function extractControllerFromRoute(array &$args, $condition = null) 263 | { 264 | // tmp remove path 265 | $path = array_shift($args); 266 | 267 | // determine prefix (eg "\Vendor\Bundle\Controller") 268 | $classNamePrefix = isset($this->settings['controller.class_prefix']) ? $this->settings['controller.class_prefix'] . '\\' : ''; 269 | 270 | // determine method suffix or default to "Action" 271 | $methodNameSuffix = isset($this->settings['controller.method_suffix']) ? $this->settings['controller.method_suffix'] : 'Action'; 272 | $methodName = ''; 273 | $className = array_shift($args); 274 | if (strpos($className, '\\') !== 0) { 275 | $className = $classNamePrefix . $className; 276 | } 277 | 278 | // having : 279 | if (preg_match('/^([a-zA-Z0-9\\\\_]+):([a-zA-Z0-9_]+)$/', $className, $match)) { 280 | $className = $match[1]; 281 | $methodName = $match[2] . $methodNameSuffix; 282 | } 283 | 284 | // malformed 285 | else { 286 | throw new \InvalidArgumentException("Malformed class action for '$className'. Use 'className:methodName' format."); 287 | } 288 | 289 | // build & append callable 290 | $app = &$this; 291 | $callable = function () use ($app, $className, $methodName, $path) { 292 | $args = func_get_args(); 293 | $instance = new $className($app); 294 | 295 | return call_user_func_array(array($instance, $methodName), $args); 296 | }; 297 | /*if (!is_null($condition)) { 298 | array_push($args, $condition); 299 | }*/ 300 | array_push($args, $callable); 301 | 302 | // re-add path 303 | array_unshift($args, $path); 304 | 305 | return; 306 | } 307 | 308 | public function urlFor($name, $params = array()) 309 | { 310 | return sprintf('/%s%s', $this->view()->getLang(), parent::urlFor($name, $params)); 311 | } 312 | 313 | } 314 | -------------------------------------------------------------------------------- /src/Rapyd/Widgets/DataForm.php: -------------------------------------------------------------------------------- 1 | 'form'); 22 | protected $error_string; 23 | protected $form_scripts; 24 | 25 | /** 26 | * it get an identifier, instance a validation library and check if a model is passed (in the config-array) 27 | * 28 | * @param array $config 29 | */ 30 | public function __construct($config = array()) 31 | { 32 | parent::__construct($config); 33 | $this->cid = parent::getIdentifier(); 34 | $this->validation = new rpd_validation_library(); 35 | $url = ($this->url != '') ? $this->url : url_helper::get_url(); 36 | $this->process_url = url_helper::append('process', 1, $url); 37 | if (isset($this->model)) { 38 | $this->status = "create"; 39 | if (isset($this->model)) { 40 | $this->status = "create"; 41 | } 42 | } 43 | } 44 | 45 | /** 46 | * called automagically by field($field) or field($type, $name, $label) 47 | * 48 | * @return object field class 49 | */ 50 | public function set_field() 51 | { 52 | $field = array(); 53 | if (func_num_args() == 3) { 54 | list($field['type'], $field['name'], $field['label']) = func_get_args(); 55 | } 56 | if (func_num_args() == 1) { 57 | $field = func_get_arg(0); 58 | } 59 | if (isset($field['field'])) { 60 | list($field['type'], $field['name'], $field['label']) = explode('|', $field['field']); 61 | } 62 | $field_name = $field["name"]; 63 | //load and instance field 64 | $field_file = strtolower($field["type"]); 65 | $field_class = $field_file . '_field'; 66 | $field_obj = new $field_class($field); 67 | if ($field_obj->type == "upload") { 68 | $this->multipart = true; 69 | if (!isset($this->upload)) { 70 | $this->upload = new upload_helper(); 71 | } 72 | $field_obj->upload = $this->upload; 73 | } 74 | //share model 75 | if (isset($this->model)) { 76 | $field_obj->model = $this->model; 77 | } 78 | //default group 79 | if (isset($this->default_group) && !isset($field_obj->group)) { 80 | $field_obj->group = $this->default_group; 81 | } 82 | $this->fields[$field_name] = $field_obj; 83 | 84 | return $field_obj; //method chaining 85 | } 86 | 87 | public function &get_field($field_name) 88 | { 89 | if (isset($this->fields[$field_name])) { 90 | return $this->fields[$field_name]; 91 | } else { 92 | $this->show_error('datamodel non valido ' . get_class($source)); 93 | die(); 94 | } 95 | } 96 | 97 | /** 98 | * set css style 99 | * @todo search online for a better way to work with html/css for example using a dom-api like phpquery 100 | * @param string $style 101 | */ 102 | protected function set_style($style) 103 | { 104 | $this->set_attributes(array('style' => $style)); 105 | } 106 | 107 | /** 108 | * source can be a a table-name or a database-model 109 | * if you pass a table-name a datamodel will be instanced 110 | * if the form already contains fields the model will be shared with each field 111 | * 112 | * @param mixed $source 113 | * @return object datamodel 114 | */ 115 | public function setSource($source) 116 | { 117 | //instance or reuse a model 118 | if (is_object($source) and (is_a($source, '\\Rapyd\\Model'))) { 119 | $this->model = $source; 120 | } elseif (is_string($source)) { 121 | //$this->model = \Rapyd\Model(); 122 | //trigger error ? 123 | 124 | } else { 125 | die('invalid source'); 126 | } 127 | if (count($this->fields)) { 128 | foreach ($this->fields as $field_obj) { 129 | if (in_array($field_obj->name, $this->app->db->table($table)->columns)) { 130 | $field_obj->model = $this->model; 131 | } 132 | } 133 | } 134 | $this->validation->model = $this->model; 135 | 136 | return $this->model; 137 | } 138 | 139 | /** 140 | * shortcut for model->load 141 | * 142 | * @param mixed $id 143 | */ 144 | public function load($id) 145 | { 146 | if (isset($this->model)) { 147 | $this->model->load($id); 148 | } 149 | } 150 | 151 | /** 152 | * internal, build each field 153 | * 154 | */ 155 | public function build_fields() 156 | { 157 | foreach ($this->fields as $field) { 158 | //share status 159 | $field->status = $this->status; 160 | $field->build(); 161 | } 162 | } 163 | 164 | /** 165 | * usage: 166 | * 167 | * $edit->pre_process(array('update'), array($this, 'some_method')); 168 | * .. 169 | * function some_method($model) 170 | * { 171 | * //do checks.. etc.. 172 | * $model->set('afield', 'avalue'); 173 | * } 174 | * 175 | * 176 | * @todo replace/rename it using ->callback() or something similar 177 | * @param type $action 178 | * @param type $function 179 | * @param type $arr_values 180 | */ 181 | public function pre_process($action, $function, $arr_values = array()) 182 | { 183 | $this->model->pre_process($action, $function, $arr_values); 184 | } 185 | 186 | /** 187 | * usage: 188 | * 189 | * $edit->post_process(array('insert'), array($this, 'some_method')); 190 | * .. 191 | * function some_method($model) 192 | * { 193 | * //do checks.. etc.. 194 | * $model->set('afield', 'avalue'); 195 | * } 196 | * 197 | * 198 | * @param type $action 199 | * @param type $function 200 | * @param type $arr_values 201 | */ 202 | public function post_process($action, $function, $arr_values = array()) 203 | { 204 | $this->model->post_process($action, $function, $arr_values); 205 | } 206 | 207 | /** 208 | * internal, build each field then form 209 | * 210 | * @return string compiled form 211 | */ 212 | protected function build_form() 213 | { 214 | html_helper::css('widgets/dataform/assets/dataform.css'); 215 | $data = get_object_vars($this); 216 | $data['container'] = $this->button_containers(); 217 | $form_type = 'open'; 218 | // See if we need a multipart form 219 | foreach ($this->fields as $field_obj) { 220 | if ($field_obj instanceof upload_field) { 221 | $form_type = 'open_multipart'; 222 | break; 223 | } 224 | } 225 | // Set the form open and close 226 | if ($this->status_is('show')) { 227 | $data['form_begin'] = '
'; 228 | $data['form_end'] = '
'; 229 | } else { 230 | $data['form_begin'] = form_helper::$form_type($this->process_url, $this->attributes); 231 | $data['form_end'] = form_helper::close(); 232 | } 233 | $data['fields'] = $this->fields; 234 | 235 | return rpd::view('dataform', $data); 236 | } 237 | 238 | /** 239 | * main method it detect status, exec action and build output 240 | * 241 | * @param string $method 242 | */ 243 | public function build($method = 'form') 244 | { 245 | $this->process_url = $this->process_url . $this->hash; 246 | //detect form status (output) 247 | if (isset($this->model)) { 248 | $this->status = ($this->model->loaded) ? "modify" : "create"; 249 | } else { 250 | $this->status = "create"; 251 | } 252 | //build fields 253 | $this->build_fields(); 254 | //process only if instance is a dataform 255 | if (is_a($this, 'dataform_library')) { 256 | //build buttons 257 | $this->build_buttons(); 258 | //sniff action 259 | if (isset($_POST) && (url_helper::value('process'))) { 260 | $this->action = ($this->status == "modify") ? "update" : "insert"; 261 | } 262 | //process 263 | $this->process(); 264 | } 265 | $method = 'build_' . $method; 266 | $this->output = $this->$method(); 267 | } 268 | 269 | /** 270 | * internal, run form validation and return boolean result 271 | * 272 | * @return bool 273 | */ 274 | protected function is_valid() 275 | { 276 | //some fields mode can disable or change some rules. 277 | foreach ($this->fields as $field) { 278 | $field->action = $this->action; 279 | $field->get_mode(); 280 | if (isset($field->rule)) { 281 | if (($field->type != "upload") && $field->apply_rules) { 282 | $fieldnames[$field->name] = $field->label; 283 | $rules[$field->name] = $field->rule; 284 | } else { 285 | $field->required = false; 286 | } 287 | } 288 | } 289 | if (isset($rules)) { 290 | $this->validation->set_rules($rules); 291 | $this->validation->set_fields($fieldnames); 292 | if (count($_POST) < 1) { 293 | $_POST = array(1); 294 | } 295 | } else { 296 | return true; 297 | } 298 | $result = $this->validation->run(); 299 | $this->error_string = $this->validation->error_string; 300 | 301 | return $result; 302 | } 303 | 304 | /** 305 | * internal, process form, it there is a model try to save data 306 | * 307 | * 308 | * @todo redirect after profess are all in javascript (window.location), this is really bad, but there is some reason i don't remember 309 | * @todo move language specific messages to i18n files 310 | * @return boolean 311 | */ 312 | protected function process() 313 | { 314 | //database save 315 | switch ($this->action) { 316 | case "update": 317 | case "insert": 318 | //validation failed 319 | if (!$this->is_valid()) { 320 | $this->process_status = "error"; 321 | foreach ($this->fields as $field) { 322 | $field->action = "idle"; 323 | } 324 | 325 | return false; 326 | } else { 327 | $this->process_status = "success"; 328 | } 329 | foreach ($this->fields as $field) { 330 | $field->action = $this->action; 331 | $result = $field->auto_update(); 332 | if (!$result) { 333 | $this->process_status = "error"; 334 | $this->error_string = $field->save_error; 335 | 336 | return false; 337 | } 338 | } 339 | if (isset($this->model)) { 340 | $return = $this->model->save(); 341 | } else { 342 | $return = true; 343 | } 344 | if (!$return) { 345 | if ($this->model->preprocess_result === false) { 346 | if ($this->action_is("update")) { 347 | $this->error_string.= $this->model->error_string; 348 | } else { 349 | $this->error_string.= $this->model->error_string; 350 | } 351 | } 352 | $this->process_status = "error"; 353 | } 354 | 355 | return $return; 356 | break; 357 | case "delete": 358 | $return = $this->model->delete(); 359 | if (!$return) { 360 | if ($this->model->preprocess_result === false) { 361 | $this->error_string.= $this->model->error_string; 362 | } 363 | $this->process_status = "error"; 364 | } else { 365 | $this->process_status = "success"; 366 | } 367 | break; 368 | case "idle": 369 | $this->process_status = "show"; 370 | 371 | return true; 372 | break; 373 | default: 374 | return false; 375 | } 376 | } 377 | 378 | /** 379 | * internal, add a submit button 380 | * 381 | * @param array $config 382 | */ 383 | public function save_button($config = null) 384 | { 385 | $caption = (isset($config['caption'])) ? $config['caption'] : rpd::lang('btn.save'); 386 | $this->submit("btn_submit", $caption, "BL"); 387 | } 388 | 389 | /** 390 | * internal, used to join in a single string all error messages 391 | * 392 | * @param array $message 393 | */ 394 | protected function show_error($message) 395 | { 396 | echo '

' . implode('

', (!is_array($message)) ? array($message) : $message) . '

'; 397 | } 398 | 399 | /** 400 | * nest a content inside a form, it can be called onlt after a build() bacause it work on compiled output 401 | * 402 | * @param string $field_id id of container 403 | * @param string $content content to be nested 404 | */ 405 | public function nest($field_id, $content) 406 | { 407 | if ($this->output != "") { 408 | $nesting_point = 'id="' . $field_id . '">'; 409 | $this->output = str_replace($nesting_point, $nesting_point . $content, $this->output); 410 | } 411 | } 412 | 413 | } 414 | -------------------------------------------------------------------------------- /src/Rapyd/Widgets/Widget.php: -------------------------------------------------------------------------------- 1 | array(), "BL" => array(), "BR" => array()); 29 | public $buttons = array(); 30 | public $url; 31 | 32 | public function __construct($config = array()) 33 | { 34 | if (count($config) > 0) { 35 | $this->initialize($config); 36 | } 37 | $this->parser = new \Rapyd\Helpers\Parser; 38 | } 39 | 40 | /** 41 | * called by constructor, a widget can be built using an associative array to prefill each widget property 42 | * it works calling $widget->set_$key($val) for each item in config array. 43 | * since each widget is initialized this way, you can for example build a complex "dataedit" 44 | * using a sigle multi-dimensional config array, defining form properties and fields properties 45 | * (so this configuration can be stored or manipulated in an easy way) 46 | *   47 | * 48 | * //edit 49 | * $config = array ( 50 | * 'label' => 'Manage Article', 51 | * 'source' => 'articles', 52 | * 'back_url' => $this->url('filtered_grid'), 53 | * 'fields' => array ( 54 | * array ( 55 | * 'type' => 'input', 56 | * 'name' => 'title', 57 | * 'label' => 'Title', 58 | * 'rule' => 'trim|required', 59 | * ), 60 | * 61 | * ), 62 | * 'buttons' => array('modify','save','undo','back'), 63 | * ); 64 | * $edit = new \Rapyd\Widget\DataEdit($config); 65 | * $edit->build(); 66 |  *  67 | * 68 | * @param array $config 69 | */ 70 | public function initialize($config = array()) 71 | { 72 | $this->clear(); 73 | foreach ($config as $key => $val) { 74 | if (method_exists($this,'set' . ucfirst($key))) { 75 | call_user_func_array(array($this, 'set' . ucfirst($key)), (array) $val); 76 | } 77 | } 78 | } 79 | 80 | /** 81 | * reset each property to default values 82 | */ 83 | public function clear() 84 | { 85 | $vars = get_class_vars(get_class($this)); 86 | 87 | foreach ($vars as $property => $value) { 88 | if (!in_array($property, array("cid", "built"))) { 89 | if (!isset($this::$$property)) 90 | $this->$property = $value; 91 | } 92 | } 93 | } 94 | 95 | /** 96 | * identifier is empty or a numeric value, it "identify" a single object instance. 97 | * by default if you build 3 widget in a single controller/page their id will be: 98 | * "" for ther first one 99 | * 1 for the second one 100 | * 2 for the third 101 | * .. etcetera 102 | * 103 | * identifiers are used to preserve uri/url segments consistence/isolation 104 | * so for example you can build 2 datagrid on a single controller without problem because uri-semantic will be: 105 | * /controller/method/[[orderby/article_id/desc/pag/1]]/[[orderby1/anotherfield/desc/pag1/2]] 106 | * 107 | * @return string identifier 108 | */ 109 | protected function getIdentifier() 110 | { 111 | if (self::$identifier < 1) { 112 | self::$identifier++; 113 | 114 | return ""; 115 | } 116 | 117 | return (string) self::$identifier++; 118 | } 119 | 120 | /** 121 | * dynamic getter & setter 122 | * 123 | * it's used basically to ensure method chaining and to get & set widget's properties 124 | * it also enable "short array" syntax so you can use $widget->method('param|param') and it will call 125 | * $widget->set_method('param','param') 126 | * 127 | * @param string $method 128 | * @param array $arguments 129 | * @return object $this 130 | */ 131 | public function __call($method, $arguments) 132 | { 133 | $prefix = strtolower(substr($method, 0, 3)); 134 | $property = strtolower(substr($method, 3)); 135 | if (method_exists($this, 'set' . ucfirst($method))) { 136 | return call_user_func_array(array($this, 'set' . ucfirst($method)), $arguments); 137 | } 138 | 139 | if (empty($prefix) || empty($property)) { 140 | return; 141 | } 142 | 143 | if ($prefix == "get" && isset($this->$property)) { 144 | return $this->$property; 145 | } 146 | 147 | if ($prefix == "set") { 148 | if 149 | ( 150 | !in_array($property, array('cell_template', 'pattern')) 151 | and is_string($arguments[0]) 152 | and strpos($arguments[0], '|') 153 | ) 154 | { 155 | //die ($property); 156 | $this->$property = explode('|', $arguments[0]); 157 | } else { 158 | $this->$property = $arguments[0]; 159 | } 160 | 161 | return $this; 162 | } 163 | } 164 | 165 | /** 166 | * shortcut for $widget->status == $status OR .... 167 | * so you can use $widget->status_is($status) 168 | * where $status can be an array of valid status 169 | * not very useful 170 | * 171 | * @todo merge all "property_is/on" in one call 172 | * @param string $status 173 | * @return bool 174 | */ 175 | public function status_is($status = "false") 176 | { 177 | if (is_array($status)) 178 | return (bool) in_array($this->status, $status); 179 | return ($this->status == $status); 180 | } 181 | 182 | /** 183 | * shortcut for $widget->process_status == $process_status OR .... 184 | * so you can use $widget->on($process_status) 185 | * where $process_status can be an array of valid status 186 | * 187 | * @todo merge all "property_is/on" in one call 188 | * @param string $process_status 189 | * @return bool 190 | */ 191 | public function on($process_status = "false") 192 | { 193 | if (is_array($process_status)) 194 | return (bool) in_array($this->process_status, $process_status); 195 | return ($this->process_status == $process_status); 196 | } 197 | 198 | /** 199 | * shortcut for $widget->action == $action OR .... 200 | * so you can use $widget->action_is($action) 201 | * where $process_status can be an array of valid actions 202 | * not very useful 203 | * 204 | * @todo merge all "property_is/on" in one call 205 | * @param string $process_status 206 | * @return bool 207 | */ 208 | public function action_is($action = "false") 209 | { 210 | if (is_array($action)) 211 | return (bool) in_array($this->action, $action); 212 | return ($this->action == $action); 213 | } 214 | 215 | /** 216 | * alias for action_is 217 | * 218 | * @param string $action 219 | * @return bool 220 | */ 221 | public function when($action = "false") 222 | { 223 | return $this->action_is($action); 224 | } 225 | 226 | /** 227 | * important stuff, widgets support placeholders like: {placeholder} 228 | * parse_pattern find all occurences and return a simple array of matches 229 | * it's used for example to find "field" placeholders inside a datagrid column pattern 230 | * 231 | * @param string $pattern 232 | * @return array of matches {placeholders} 233 | */ 234 | public static function parse_pattern($pattern) 235 | { 236 | if (preg_match_all('/\{(\w+)\}/is', $pattern, $matches)) { 237 | return $matches[1]; 238 | } 239 | } 240 | 241 | /** 242 | * replace placeholders in a pattern like "bla bla.. {placeholder}" 243 | * 244 | * @param string $pattern a string containing {placeholder}s 245 | * @param array $values associative array like array('placeholder'=>'value', 'placeholder2'=>'value2') 246 | * @return string parsed output 247 | */ 248 | public static function replace_pattern($pattern, $values) 249 | { 250 | $output = $pattern; 251 | $output = str_replace('|', '£|£', $output); //fix for params separator 252 | $matches = self::parse_pattern($pattern); 253 | if ($matches) { 254 | foreach ($matches as $field) { 255 | if (isset($values[$field]) or is_null($values[$field])) 256 | $output = str_replace('{' . $field . '}', $values[$field], $output); 257 | } 258 | } 259 | 260 | return $output; 261 | } 262 | 263 | 264 | /** 265 | * basically it "flat" buttons array for each zone 266 | * used on build() to prepare button output 267 | * zones are "TL","TR","BL","BR" (top-left, top-right, bottom-left, bottom-right) 268 | * see button() 269 | * 270 | * @todo i think it must be improved.. 271 | * @param string $container 272 | * @return array 273 | */ 274 | public function button_containers($container = null) 275 | { 276 | if (isset($container)) { 277 | return join(" ", $this->button_container[$container]); 278 | } else { 279 | foreach ($this->button_container as $container => $content) { 280 | $containers[$container] = join(" ", $this->button_container[$container]); 281 | } 282 | 283 | return $containers; 284 | } 285 | } 286 | 287 | /** 288 | * add a button to the button container in a given position 289 | * positions are "TL","TR","BL","BR" (top-left, top-right, bottom-left, bottom-right) 290 | * 291 | * @param string $name button name attribute 292 | * @param string $caption button label 293 | * @param string $action the onclick event (all buttons currently works using window.location.href=...) 294 | * @param string $position "TL","TR","BL","BR" 295 | * @param string $class css class 296 | */ 297 | public function button($name, $caption, $action, $position="BL", $class="btn") 298 | { 299 | $this->button_container[$position][] = html_helper::button($name, $caption, $action, "button", $class); 300 | } 301 | 302 | /** 303 | * same of button() but it make a submit 304 | * 305 | * @param string $name 306 | * @param string $caption 307 | * @param string $position 308 | */ 309 | public function submit($name, $caption, $position="BL") 310 | { 311 | $this->button_container[$position][] = html_helper::button($name, $caption, "", "submit", "btn"); 312 | } 313 | 314 | /** 315 | * 316 | * @param type $config 317 | */ 318 | public function action_button($config) 319 | { 320 | 321 | $caption = (isset($config['caption'])) ? $config['caption'] : "Azione"; 322 | if (isset($config['popup']) || isset($config['target'])) { 323 | $config['popup'] = (isset($config['popup'])) ? ",'mywin','" . $config['popup'] . "'" : ""; 324 | $action = "javascript:window.open('" . $config['url'] . "'" . $config['popup'] . ")"; 325 | } else { 326 | if (isset($config['confirm'])) { 327 | $action = "javascript:if (confirm('" . addslashes($config['confirm']) . "')) { window.location='" . $config['url'] . "' }"; 328 | } else { 329 | $action = "javascript:window.location='" . $config['url'] . "'"; 330 | } 331 | } 332 | $position = (isset($config['position'])) ? $config['position'] : "TR"; 333 | $class = (isset($config['class'])) ? $config['class'] : "btn"; 334 | $this->button("btn_act", $caption, $action, $position, $class); 335 | } 336 | 337 | 338 | /** 339 | * it exppect parameters like a string, an array, a serialized array 'save|delete|undo...' 340 | * or an associative array, and fill the "$widget->buttons" array 341 | * then buil_buttons will cycle this array to make buttons 342 | * 343 | */ 344 | public function set_buttons() 345 | { 346 | 347 | $buttons = func_get_args(); 348 | 349 | //catch buttons => 'save|delete|undo...'; 350 | if (is_string($buttons[0]) and strpos($buttons[0], '|')) { 351 | $buttons = explode('|', $buttons[0]); 352 | } 353 | 354 | if (func_num_args() == 1 and is_array($buttons[0])) { 355 | $buttons = $buttons[0]; 356 | } 357 | 358 | foreach ($buttons as $name => $button) { 359 | if (is_numeric($name) and is_string($button)) { 360 | if (strpos($button, '|')) { 361 | list($button['type'], $button['caption']) = explode('|', $button); 362 | } else { 363 | $button = array('type' => $button); 364 | } 365 | } else { 366 | if (is_string($button) and strpos($button, '|')) { 367 | $btn = array(); 368 | @list($btn['name'], $btn['caption'], $btn['url'], $btn['position'], $btn['class']) = explode('|', $button); 369 | $button = $btn; 370 | } 371 | if (!isset($button['type'])) 372 | $button['type'] = $name; 373 | } 374 | 375 | $this->buttons[] = $button; 376 | } 377 | } 378 | 379 | 380 | /** 381 | * this function cycle $widget->buttons array and call all "xxx_button()" methods 382 | * 383 | * @todo I should really consider moving it from here 384 | */ 385 | public function build_buttons() 386 | { 387 | foreach ($this->buttons as $button) { 388 | $build_button = $button['type'] . "_button"; 389 | if (count($button) < 2) { 390 | $this->$build_button(); 391 | } else { 392 | $this->$build_button($button); 393 | } 394 | } 395 | $this->buttons = array(); 396 | } 397 | 398 | /** 399 | * "echo $widget" automatically call build() it and display $widget->output 400 | * however explicit build is preferred for a clean code 401 | * 402 | * @return string 403 | */ 404 | public function __toString() 405 | { 406 | if ($this->output == "") 407 | $this->build(); 408 | 409 | return $this->output; 410 | } 411 | 412 | } 413 | --------------------------------------------------------------------------------