├── README ├── .gitignore └── models ├── readme.md ├── Data ├── Null.php └── Phone.php ├── User.php └── Data.php /README: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | -------------------------------------------------------------------------------- /models/readme.md: -------------------------------------------------------------------------------- 1 | # Saving 2 | 3 | ```php 4 | fill(array( 8 | 'fullname' => Input::get('fullname'), 9 | 'email' => Input::get('email'), 10 | 'password' => Input::get('password'), 11 | )); 12 | 13 | $phone = Data::factory('phone', Input::get('phone') ); 14 | 15 | 16 | if ($user->valid() && $phone->valid()) { 17 | $user->save(); 18 | $phone->user_id = $user->id; 19 | $phone->save(); 20 | } 21 | 22 | ``` 23 | 24 | 25 | # Retrieval 26 | 27 | 28 | ```php 29 | all_phones()->get(); 34 | 35 | $home_phone = $user->all_phones('home/voice')->first(); 36 | 37 | ``` -------------------------------------------------------------------------------- /models/Data/Null.php: -------------------------------------------------------------------------------- 1 | 'Work', 16 | 'home/voice' => 'Home', 17 | 'cell/voice' => 'Mobile', 18 | 'work/fax' => 'Work Fax', 19 | 'home/fax' => 'Home Fax', 20 | 'pager' => 'Pager', 21 | 'other/voice' => 'Other', 22 | ); 23 | 24 | 25 | // this are attributes that we'd like (for ease of use), but that get 26 | // serialized/unserialized when saving/loading 27 | 28 | public $temporary = array( 29 | 'number', 30 | 'extension', 31 | ); 32 | 33 | 34 | // we can make rules and messages for these attributes too: 35 | 36 | public static $rules = array( 37 | 'number' => 'required|phone', 38 | 'extension' => 'integer', 39 | ); 40 | 41 | public static $messages = array( 42 | 'number_phone' => 'Phone numbers can start with a "+", and only contain 0-9, "-", "." or " "', 43 | 'extension_integer' => 'Extensions can only contain numbers', 44 | ); 45 | 46 | 47 | 48 | } -------------------------------------------------------------------------------- /models/User.php: -------------------------------------------------------------------------------- 1 | 'required|prestoh_username|unique:users|routesafe', 9 | 'email' => 'required|email', 10 | 'password' => 'required|min:6', 11 | 'fullname' => 'required', 12 | 'firstname' => '', 13 | 'lastname' => '', 14 | ); 15 | 16 | 17 | public static $messages = array( 18 | 'email' => 'A valid email address is required', 19 | 'fullname_required' => 'Your full name is required', 20 | 'password_min' => 'Minimum 6 characters', 21 | 'username_routesafe' => 'That username has already been taken', 22 | 'username_valid_username' => 'Usernames can contain letters, numbers, "-", "_" or "."', 23 | ); 24 | 25 | 26 | 27 | /** 28 | * Define model relationships 29 | */ 30 | 31 | public function data() 32 | { 33 | return $this->has_many('Data'); 34 | } 35 | 36 | 37 | /** 38 | * Magic method to filter data values 39 | * e.g.: $user->all_phones(); 40 | * $user->all_addresses(); 41 | * 42 | */ 43 | public function __call($name, $arguments) 44 | { 45 | if (substr($name, 0, 4)=='all_') { 46 | $class = Str::singular(substr($name,4)); 47 | $dclass = 'Data_'.$class; 48 | 49 | $return = $dclass::where('user_id','=',$this->id)->where('class','=',$class); 50 | 51 | if (count($arguments)) { 52 | $type = $arguments[0]; 53 | $return = $return->where('type','=',$type); 54 | } 55 | 56 | return $return; 57 | } 58 | return parent::__call($name,$arguments); 59 | } 60 | 61 | 62 | } 63 | -------------------------------------------------------------------------------- /models/Data.php: -------------------------------------------------------------------------------- 1 | 'required|exists:users,id', 15 | 'class' => 'required', 16 | 'type' => 'required', 17 | 'string' => 'required', 18 | ); 19 | 20 | 21 | // overload this in the extended class with all the fields we want to serialize 22 | // added benefit: the Aware bundle won't try and save these attributes into the DB 23 | 24 | public $temporary = array(); 25 | 26 | 27 | // this is where we'll temporarily store the unserialized data 28 | 29 | protected $unserialized_data = array(); 30 | 31 | 32 | public function __construct($attributes = array()) 33 | { 34 | 35 | // automagically build the "type" rules based on the "types" attribute 36 | 37 | if (count(static::$types)) { 38 | static::$rules['type'] = array( 39 | 'required', 40 | 'in:' . join(',', array_keys(static::$types)) 41 | ); 42 | } 43 | 44 | parent::__construct($attributes); 45 | 46 | } 47 | 48 | 49 | 50 | /** 51 | * A factory method that will automatically "cast" the data to the correct type. 52 | */ 53 | 54 | public static function factory() 55 | { 56 | 57 | $args = func_get_args(); 58 | 59 | if (count($args)==0) { 60 | 61 | // no args? must just want a copy of itself (not sure why) 62 | 63 | return new static; 64 | } 65 | 66 | // okay, we're trying to make something. get the class type 67 | 68 | $classname = 'Data_'.ucfirst($args[0]); 69 | 70 | if (count($args)==1) { 71 | // we're just creating a new class 72 | return new $classname; 73 | } 74 | 75 | // okay, so we're also passing data ... let's check it 76 | // if it's empty, return a Data_Null model instead of what they 77 | // really asked for 78 | 79 | $attributes = $args[1]; 80 | 81 | // todo: check if $data isn't an array? 82 | 83 | if (count(array_filter($attributes))) { 84 | return new $classname($attributes); 85 | } 86 | 87 | return new Data_Null; 88 | 89 | } 90 | 91 | 92 | 93 | 94 | /** 95 | * expand 96 | * Converts the serialized data into regular attributes 97 | * 98 | * @param string $key 99 | * @return mixed 100 | */ 101 | public function expand() { 102 | $this->unserialized_data = json_decode($this->string); 103 | } 104 | 105 | /** 106 | * compact 107 | * Converts the regular attributes into serialized data 108 | * 109 | * @param string $key 110 | * @return mixed 111 | */ 112 | public function compact() { 113 | 114 | // update the dirty fields first 115 | // because of Aware, these will be in "ignore" 116 | 117 | foreach ($this->temporary as $key) { 118 | if( isset($this->ignore[$key]) ) { 119 | $this->unserialized_data[$key] = $this->ignore[$key]; 120 | } 121 | } 122 | 123 | $this->string = json_encode($this->unserialized_data); 124 | } 125 | 126 | 127 | 128 | /** 129 | * __get 130 | * Overrides the parent method to look for serialized data first 131 | * 132 | * @param string $key 133 | * @return mixed 134 | */ 135 | public function __get($key) { 136 | 137 | // expand if we haven't yet 138 | 139 | if ( !is_array($this->unserialized_data) ) { 140 | $this->expand(); 141 | } 142 | 143 | // are we trying to get a key from one of the temporary attributes, 144 | // and does it exist? if so, return it 145 | 146 | if ( in_array($key, $this->temporary) && array_key_exists($key, $this->unserialized_data) ) { 147 | return $this->unserialized_data[$key]; 148 | } 149 | 150 | // else, default to parent method 151 | 152 | return parent::__get($key); 153 | 154 | } 155 | 156 | 157 | /** 158 | * Save. 159 | * Should automatically serialize the attributes into data.string. 160 | * 161 | * @param int $count 162 | * @return array 163 | */ 164 | public function save($rules=array(), $messages=array()) 165 | { 166 | 167 | // serialize the data and then save 168 | 169 | $this->compact(); 170 | 171 | // force the class 172 | 173 | $this->class = static::$class; 174 | 175 | // force the type if it isn't required 176 | 177 | if (!count(static::$types)) { 178 | $this->type = ''; 179 | } 180 | 181 | parent::save($rules, $messages); 182 | } 183 | 184 | 185 | } 186 | --------------------------------------------------------------------------------