Marcel
The PHP 5.4 MVC with Shoes On: ActiveRecord, User/Session, Generators, SCSS, (Twitter Bootstrap) Integration, Workers, Cron Management, Image Manipulation, Caching, Git Management, Mail & Mail Parsing, OCR, Scraping, Selenium, Mustache, Markdown, Phone & Text Messaging, WebSockets, BitTorrent and much more!
Install / Use
/learn @dancrew32/MarcelREADME
Marcel
The MVC with Shoes On

Contents stable
- Requirements
- Install
- VirtualHost Setup
- Scripts
- Routing
- Models
- Controllers
- Views
- Assets
- SCSS & Compass
- Cookies & Notes
- Cache
- Form Fields
- Image Manipulation
- Utilities
- Helpers
- Cron
- Workers
- Mail Parsing
- Fake Data
- CAPTCHA
- OCR
- Scraping
- Phone Calls & Text Messaging
- Interactive Prompt
- Vim Interactivity
- WebSocket Server
- Linode
Contents unstable
Contents future
- USPS
- Geolocation
- Stocks
- Cart
- Stripe
- FFMPEG
- Waveform Generation
- Geometry
- Vimeo/Youtube
- Bitcoin
- Travis CI
- RSS
- Emoji
- Color Manipulation
- IRC/Jabber
- Face Detection
- AWS
- App Engine
Requirements
- PHP 5.4
- MySQL 5.5
- Apache 2.2
- Ruby Gems
I only have PHP 5.3
C'mon! Upgrading is easy:
sudo apt-get install python-software-properties
sudo add-apt-repository ppa:ondrej/php5
sudo apt-get update
sudo apt-get upgrade
# To update APC (if you use that instead of x-cache)
# sudo pecl install apc
Install
Clone and run the db init wizard:
git clone git@github.com:dancrew32/marcel.git site
cd site
git submodule update
php script/db_init.php
chmod 777 -R tmp .git
chmod 777 marcel vendor .gitmodules
cat config/api.php.example > config/api.php
After install, it will prompt you to seed the database with defaults
and create your first user. You should also set your
public/index.php and BASE_URL.
VirtualHost Setup
<VirtualHost *:80>
ServerName site.com
DocumentRoot /var/www/site/public
SetEnv ENV "DEV" # or "LIVE"
</VirtualHost>
# SSL Version
<VirtualHost *:443>
ServerName site.com
DocumentRoot /var/www/site/public
SSLEngine on
SSLCertificateFile /path/to/your.crt
SSLCertificateKeyFile /path/to/your.key
</VirtualHost>
Scripts
Marcel has a ton of scripts available to automate development.
Just run ./m <sitename> (so maybe ./m marcel for site/marcel) from the root directory to get a menu
of scripts to run!
When you're comfortable with the
list of scripts you have, use the search shortcut
to immediately run that script ./m <site> <search>
(so maybe ./m <site> dbdump to run php site/<site>/script/db_dump.php).
Every script is an easy to use interactive wizard:
Wizards & Scripts
Wizard | Script Description
--- | ---
php site/marcel/script/gen_controller.php | Controller
php site/marcel/script/gen_model.php | Model
php site/marcel/script/gen_view.php | View
php site/marcel/script/gen_js_class.php | JavaScript Module
php site/marcel/script/gen_script.php | Script/Cron
php site/marcel/script/db_init.php | DB initialization (see Install)
php site/marcel/script/db_create_mysql_user.php | Create a new MySQL user with permissions to only this DB_NAME
php site/marcel/script/db_dump.php | DB dump in db/dump
php site/marcel/script/db_restore.php | DB restore from db/dump
php site/marcel/script/db_schema_apply.php | DB apply a schema in from db/schema
php site/marcel/script/db_schema_update.php | DB update all schemas in from db/schema
php site/marcel/script/create_user.php | Create Users (e.g. Create your first User with role of admin)
php site/marcel/script/cron.base.php | Run each Cron_Job if Cron_Job->frequency matches time() and is active
php site/marcel/script/scss_watch.php | Run compass watch as daemon to watch SCSS
php site/marcel/script/worker.php | Start a Worker server
php site/marcel/script/vim.php | Start an Interactive Vim eval session
php site/marcel/script/fake_users.php | Create 250 fake Users with role of user
Routing
In routes.php, we send url $_SERVER['REQUEST_URI']
preg_matches
to a specified method in a controller.
By default, routing is simple, but you may increase the complexity if you would like
HTTP method granularity
and/or auth class permissions handled at the router
(instead of the controller).
You may capture parameters using regular expressions with
named subpatterns
e.g. '/(?P<word>\w+)/(?P<digit>\d+)' would match /blogs/2.
Take a look at class/route.php to see all of the possibilities.
Route Keys
Key | Description
--- | ---
c | Controller required
m | Method required
l | Layout (foo would be view/layout/foo.php)
auth | Authorization Feature->slug via class/auth.php to gate access with
name | Unique name for this route (see route::get($name, $params) useful when URL paths change)
section | Name for grouping routes together (e.g. Portfolio) (see route::in_sections(['A', 'B'])) http| for nested [REST](https://en.wikipedia.org/wiki/Representational_state_transfer) routing (e.g.get, post, put, delete) nodb| iftrue`, skip any database connections for this execution
Example Route Implementation
<?
route::$routes = [
# Site base url leads to controller_yours::foo
'/' => ['c' => 'yours', 'm' => 'foo'],
# Capture page id, name capture "id" with (?P<capturename>regexp) syntax
'/page/(?P<id>\d+)' => ['c' => 'yours', 'm' => 'test'],
# HTTP method-specific (Optional)
'/http' => [
'http' => [
'get' => [ 'c' => 'http_test', 'm' => 'get' ],
'post' => [ 'c' => 'http_test', 'm' => 'post' ],
'put' => [ 'c' => 'http_test', 'm' => 'put' ],
'delete' => [ 'c' => 'http_test', 'm' => 'delete' ],
],
],
# Skip Database (`nodb` avoids database & user session initialization)
'/i' => [ 'c' => 'image', 'm' => 'process', 'nodb' => true, 'name' => "Image Process" ],
# Auth (optional)
'/auth-test-simple' => [
'c' => 'common', 'm' => 'auth_test',
'auth' => ['feature_name'], # only users who can do "feature_name"
],
'/auth-test-complex' => [
'http' => [
'get' => [
'c' => 'common', 'm' => 'auth_test',
'auth' => ['thing_a'], # only "thing_a" feature-allowed users may GET
],
'post' => [
'c' => 'common', 'm' => 'auth_test',
'auth' => ['thing_b'], # only "thing_b" feature-allowed users may POST
],
],
'auth' => ['thing_c'], # "thing_c" may GET and POST
],
# Named-Routes & Sections (optional)
'/changes-frequently' =>
[ 'c' => 'thing' => 'm' => 'index', 'name' => 'Things Home', 'section' => 'Things' ],
# route::get('Things Home')
# returns '/changes-frequently'
'/changes-as-well(?:/*)(?P<url_slug>\d+)' =>
[ 'c' => 'thing' => 'm' => 'secondary', 'name' => 'Things Secondary', 'section' => 'Things' ],
# route::get('Things Secondary', ['url_slug' => 'true-story'])
# returns '/changes-as-well/true-story'
# route::get('Things Secondary', ['url_slug' => 'true-story', 'foo' => 'bar'])
# returns '/changes-as-well/true-story?foo=bar'
];
Models (M)
Uses PHPActiveRecord
(see Basic CRUD to learn more).
Get started with a new Model using the php script/gen_model.php Script.
Simple Model Example
<?
class Thing extends model {
static $table_name = 'things';
}
# Create: http://www.phpactiverecord.org/projects/main/wiki/Basic_CRUD#create
$t = new Thing;
$t->stuff = "raisin";
$t->save();
# Read: http://www.phpactiverecord.org/projects/main/wiki/Finders
$b = Thing::find(1);
echo $b->stuff; # "raisin"
# Update: http://www.phpactiverecord.org/projects/main/wiki/Basic_CRUD#update
$b->stuff = "dorito";
$b->save();
# Destroy: http://www.phpactiverecord.org/projects/main/wiki/Basic_CRUD#delete
$b->delete();
Complex Model Example
<?
class Stuff extends model {
static $table_name = 'stuff';
/*
* RELATIONSHIPS
* Read More: http://www.phpactiverecord.org/projects/main/wiki/Associations
*/
# `thing_id` in `stuff_types` table: $stuff->type (Stuff_Type object)
static $has_one = [
[ 'type', 'class_name' => 'Stuff_Type' ],
];
# `thing_id` in `owners` table: $stuff->owners (collection of Owner objects)
static $has_many = [
[ 'owners', 'class_name' => 'Owner' ],
];
# `thing_id` in `stuff` table: $stuff->thing (Thing object)
static $belongs_to = [
[ 'thing', 'class_name' => 'Thing' ],
];
/*
* VALIDATION
* Read More: http://www.phpactiverecord.org/projects/main/wiki/Validations
*/
# Existence
static $validates_presence_of = [
['name',
'message' => 'must be present!'], # "Name must be present!"
];
# Length
static $validates_size_of = [
# Exact
['field_a',
'is' => 42,
'message' => 'must be exactly 42 chars'], # "Field_a must be exactly 42 chars"
# Minimum
['field_b',
'minimum' => 9,
'too_short' => 'must be at least 9 characters long'],
# Maximum
['field_c',
'maximum' => 20,
'too_long' => 'is too long!'],
# Min/Max
['field_d',
'within' => [5, 10],
'too_short' => 'must be longer than 5 (less than 10)',
Related Skills
node-connect
349.9kDiagnose OpenClaw node connection and pairing failures for Android, iOS, and macOS companion apps
frontend-design
109.8kCreate distinctive, production-grade frontend interfaces with high design quality. Use this skill when the user asks to build web components, pages, or applications. Generates creative, polished code that avoids generic AI aesthetics.
openai-whisper-api
349.9kTranscribe audio via OpenAI Audio Transcriptions API (Whisper).
qqbot-media
349.9kQQBot 富媒体收发能力。使用 <qqmedia> 标签,系统根据文件扩展名自动识别类型(图片/语音/视频/文件)。
