<commit_before><?php namespace Bkwld\Decoy\Models;

// Imports
use App;
use Bkwld\Library\Utils\File;
use Bkwld\Library\Utils\Collection;
use Config;
use Croppa;
use DB;
use Event;
use Illuminate\Database\Eloquent\Model as Eloquent;
use Input;
use Log;
use Request;
use Str;

abstract class Base extends Eloquent {
	
	//---------------------------------------------------------------------------
	// Overrideable properties
	//---------------------------------------------------------------------------
	
	// This should be overridden by Models to store the array of their 
	// Laravel validation rules
	static public $rules = array();
	
	// This is designed to be overridden to store the DB column name that
	// should be used as the source for titles.  Used in the title() function
	// and in autocompletes.
	static public $TITLE_COLUMN;
	
	// This is should be overriden like so to specify crops that the image cropping
	// widget should make UI for
	// array('image' => array('marquee' => '4:3', 'feature'))
	static public $CROPS = array();
	
	public function __construct(array $attributes = array()) {
		parent::__construct($attributes);
		
		// Register Laravel model events
		static::registerModelEvents();
		
		// Blacklist special columns that aren't intended for the DB
		$this->guarded = array_merge($this->guarded, array(
			'_token', // Part of CSRF protection
			'_wysihtml5_mode',
			'_save', // The submit buttons, tells us which submit button they clicked
			'parent_controller', // Backbone.js sends this with sort updates
		));
	}
	
	//---------------------------------------------------------------------------
	// Model event callbacks
	//---------------------------------------------------------------------------
	// Setup listeners for all of Laravel's built in events that fire our no-op
	// callbacks. These listeners are created when the first instance of a model
	// is created.
	
	// Listen for model events and execute no-op callbacks on the model instance
	static private $models_registered_for_events = array();
	static private function registerModelEvents() {
		
		// Only run once per class.  Having to store in an array like this because
		// setting a boolean on the class kept setting it to true for all models that
		// inherit from Base.  I couldn't seem to set the boolean on the child only.
		$class = get_called_class();
		if (in_array($class, self::$models_registered_for_events)) return;
		self::$models_registered_for_events[] = $class;
		
		// Built in Laravel model events
		self::creating (function($model){ return $model->onCreating(); });
		self::created  (function($model){ return $model->onCreated(); });
		self::updating (function($model){ return $model->onUpdating(); });
		self::updated  (function($model){ return $model->onUpdated(); });
		self::saving   (function($model){ return $model->onSaving(); });
		self::saved    (function($model){ return $model->onSaved(); });
		self::deleting (function($model){ return $model->onDeleting(); });
		self::deleted  (function($model){ return $model->onDeleted(); });
		
		// Decoy events
		$events = array('validating', 'validated', 'attaching', 'attached', 'removing', 'removed');
		foreach ($events as $event) {
			Event::listen('decoy.'.$event.': '.$class, function($model = null) use ($event) {
				
				// It's possible a model wasn't defined
				if (!$model) return;
				
				// Call the appropriate model callback with all other arguments
				$args = array_slice(func_get_args(), 1);
				$callback = 'on'.ucfirst($event);
				return call_user_func_array(array($model, $callback), $args);
			});
		}
		
	}
	
	// The no-op callbacks.  They have to be defined as public because they are invoked 
	// from anonymous functions.
	public function onSaving() {}
	public function onSaved() {}
	public function onValidating($input) {}
	public function onValidated($input) {}
	public function onCreating() {}
	public function onCreated() {}
	public function onUpdating() {}
	public function onUpdated() {}
	public function onDeleting() {}
	public function onDeleted() {}
	public function onAttaching() {}
	public function onAttached($pivot) {}
	public function onRemoving($pivot) {}
	public function onRemoved($pivot) {}
	
		
	//---------------------------------------------------------------------------
	// Overrideable methods
	//---------------------------------------------------------------------------
	
	// Return the title for the row for the purpose of displaying
	// in admin list views and breadcrumbs.  It looks for columns
	// that are named like common things that would be titles
	public function title() {
		$title = '';
		
		// Add a thumbnail to the title if there is an "image" field
		if (method_exists($this, 'image') && $this->image()) $title .= '<img src="'.Croppa::url($this->image(), 40, 40).'"/> ';
		elseif (!method_exists($this, 'image') && !empty($this->image)) $title .= '<img src="'.$this->croppa(40,40).'"/> ';
		
		// Convert to an array so I can test for the presence of values.
		// As an object, it would throw exceptions
		$row = $this->toArray();
		if (!empty(static::$TITLE_COLUMN)) $title .=  $row[static::$TITLE_COLUMN];
		else if (isset($row['name'])) $title .=  $row['name']; // Name before title to cover the case of people with job titles
		else if (isset($row['title'])) $title .= $row['title'];
		else if (App::make('decoy_router')->action() == 'edit')  $title .= 'Edit';
		
		// Return the finished title
		return $title;

	}
	
	// Save out an image or file given the field name.  They are saved
	// to the directory specified in the bundle config
	public function saveImage($input_name = 'image') { return $this->saveFile($input_name); }
	public function saveFile($input_name = 'file') {
		$path = File::organizeUploadedFile(Input::file($input_name), Config::get('decoy::upload_dir'));
		$path = File::publicPath($path);
		return $path;
	}
	
	// Many models will override this to create custom methods for getting
	// a list of rows
	static public function ordered() {
		return static::orderBy(self::table_name().'.created_at', 'desc');
	}
	
	// Get an ordered list of only rows that are marked as visible
	static public function ordered_and_visible() {
		return static::ordered()->where('visible', '=', '1');
	}
	
	//---------------------------------------------------------------------------
	// Utility methods
	//---------------------------------------------------------------------------
	
	// Randomize the results in the DB.  This shouldn't be used for large datasets
	// cause it's not very performant
	static public function randomize() {
		return static::orderBy(DB::raw('RAND()'));
	}
	
	// Find by the slug.  Like "find()" but use the slug column instead
	static public function find_slug($slug) {
		return static::where(self::table_name().'.slug', '=', $slug)->first();
	}
	
	// Figure out the current table name but allow it to be called statically
	static protected function table_name() {
		$model = get_called_class();
		$model = new $model;
		return $model->getTable();
	}
	
	/**
	 * The pivot_id may be accessible at $this->pivot->id if the result was fetched
	 * through a relationship OR it may be named pivot_id out of convention (something
	 * currently done in Decoy_Base_Controller->get_index_child()).  This function
	 * checks for either
	 * @return integer 
	 */
	public function pivotId() {
		if (!empty($this->pivot->id)) return $this->pivot->id;
		else if (!empty($this->pivot_id)) return $this->pivot_id;
		else return null;
	}
	
	// Form a croppa URL, taking advantage of being able to set more columns null.  Also,
	// provides an easier way to inform the source crops
	public function croppa($width = null, $height = null, $crop_style = null, $field = 'image', $options = null) {
		
		// Check if the image field has crops
		if ($crop_style && !array_key_exists($field, static::$CROPS)) {
			throw new \Exception("A crop style was passed for $field but no crops are defined for that field.");
		}
		
		// Check if the crop style is valid
		if ($crop_style && !Collection::keyOrValueExists($crop_style, static::$CROPS[$field])) {
			throw new \Exception("Crop style '$crop_style' is not defined for the field: $field");
		}
		
		// Default crop style is 'default'
		if (!$crop_style && !empty(static::$CROPS[$field]) && Collection::keyOrValueExists('default', static::$CROPS[$field])) {
			$crop_style = 'default';
		}
		
		// Get the image src path
		if (method_exists($this, $field)) $src = call_user_func(array($this, $field));
		else $src = $this->$field;
		if (empty($src)) return;
		
		// If there is a crop value, add it to the options
		if ($crop_style) {
			$crops = json_decode($this->{$field.'_crops'});
			
			// Check if a crop style was set in the admin for this crop style
			if (!empty($crops->$crop_style)) {
				if (!is_array($options)) $options = array();
				
				// Add the trim instructions to the croppa options
				$options = array_merge($options, array(
					'trim_perc' => array(
						round($crops->$crop_style->x1,4),
						round($crops->$crop_style->y1,4),
						round($crops->$crop_style->x2,4),
						round($crops->$crop_style->y2,4),
					),
				));
			}
		}
		
		// Return the Croppa URL
		return Croppa::url($src, $width, $height, $options);
		
	}
	
	/**
	 * Get the admin controller class for this model.  It's assumed to NOT be a decoy controller.
	 * In other words, it's in app/controllers/admin/.
	 * @return string ex: Admin\ArticlesController
	 */
	static public function adminControllerClass() {
		return ucfirst(Config::get('decoy::dir')).'\\'.Str::plural(get_called_class()).'Controller';
	}
	
	/**
	 * Add a field to the blacklist
	 */
	public function blacklist($field) {
		$this->guarded[] = $field;
	}
	
}<commit_msg>Updating casing of base model methods #64<commit_after><?php namespace Bkwld\Decoy\Models;

// Imports
use App;
use Bkwld\Library\Utils\File;
use Bkwld\Library\Utils\Collection;
use Config;
use Croppa;
use DB;
use Event;
use Illuminate\Database\Eloquent\Model as Eloquent;
use Input;
use Log;
use Request;
use Str;

abstract class Base extends Eloquent {
	
	//---------------------------------------------------------------------------
	// Overrideable properties
	//---------------------------------------------------------------------------
	
	// This should be overridden by Models to store the array of their 
	// Laravel validation rules
	static public $rules = array();
	
	// This is designed to be overridden to store the DB column name that
	// should be used as the source for titles.  Used in the title() function
	// and in autocompletes.
	static public $TITLE_COLUMN;
	
	// This is should be overriden like so to specify crops that the image cropping
	// widget should make UI for
	// array('image' => array('marquee' => '4:3', 'feature'))
	static public $CROPS = array();
	
	public function __construct(array $attributes = array()) {
		parent::__construct($attributes);
		
		// Register Laravel model events
		static::registerModelEvents();
		
		// Blacklist special columns that aren't intended for the DB
		$this->guarded = array_merge($this->guarded, array(
			'_token', // Part of CSRF protection
			'_wysihtml5_mode',
			'_save', // The submit buttons, tells us which submit button they clicked
			'parent_controller', // Backbone.js sends this with sort updates
		));
	}
	
	//---------------------------------------------------------------------------
	// Model event callbacks
	//---------------------------------------------------------------------------
	// Setup listeners for all of Laravel's built in events that fire our no-op
	// callbacks. These listeners are created when the first instance of a model
	// is created.
	
	// Listen for model events and execute no-op callbacks on the model instance
	static private $models_registered_for_events = array();
	static private function registerModelEvents() {
		
		// Only run once per class.  Having to store in an array like this because
		// setting a boolean on the class kept setting it to true for all models that
		// inherit from Base.  I couldn't seem to set the boolean on the child only.
		$class = get_called_class();
		if (in_array($class, self::$models_registered_for_events)) return;
		self::$models_registered_for_events[] = $class;
		
		// Built in Laravel model events
		self::creating (function($model){ return $model->onCreating(); });
		self::created  (function($model){ return $model->onCreated(); });
		self::updating (function($model){ return $model->onUpdating(); });
		self::updated  (function($model){ return $model->onUpdated(); });
		self::saving   (function($model){ return $model->onSaving(); });
		self::saved    (function($model){ return $model->onSaved(); });
		self::deleting (function($model){ return $model->onDeleting(); });
		self::deleted  (function($model){ return $model->onDeleted(); });
		
		// Decoy events
		$events = array('validating', 'validated', 'attaching', 'attached', 'removing', 'removed');
		foreach ($events as $event) {
			Event::listen('decoy.'.$event.': '.$class, function($model = null) use ($event) {
				
				// It's possible a model wasn't defined
				if (!$model) return;
				
				// Call the appropriate model callback with all other arguments
				$args = array_slice(func_get_args(), 1);
				$callback = 'on'.ucfirst($event);
				return call_user_func_array(array($model, $callback), $args);
			});
		}
		
	}
	
	// The no-op callbacks.  They have to be defined as public because they are invoked 
	// from anonymous functions.
	public function onSaving() {}
	public function onSaved() {}
	public function onValidating($input) {}
	public function onValidated($input) {}
	public function onCreating() {}
	public function onCreated() {}
	public function onUpdating() {}
	public function onUpdated() {}
	public function onDeleting() {}
	public function onDeleted() {}
	public function onAttaching() {}
	public function onAttached($pivot) {}
	public function onRemoving($pivot) {}
	public function onRemoved($pivot) {}
	
		
	//---------------------------------------------------------------------------
	// Overrideable methods
	//---------------------------------------------------------------------------
	
	// Return the title for the row for the purpose of displaying
	// in admin list views and breadcrumbs.  It looks for columns
	// that are named like common things that would be titles
	public function title() {
		$title = '';
		
		// Add a thumbnail to the title if there is an "image" field
		if (method_exists($this, 'image') && $this->image()) $title .= '<img src="'.Croppa::url($this->image(), 40, 40).'"/> ';
		elseif (!method_exists($this, 'image') && !empty($this->image)) $title .= '<img src="'.$this->croppa(40,40).'"/> ';
		
		// Convert to an array so I can test for the presence of values.
		// As an object, it would throw exceptions
		$row = $this->toArray();
		if (!empty(static::$TITLE_COLUMN)) $title .=  $row[static::$TITLE_COLUMN];
		else if (isset($row['name'])) $title .=  $row['name']; // Name before title to cover the case of people with job titles
		else if (isset($row['title'])) $title .= $row['title'];
		else if (App::make('decoy_router')->action() == 'edit')  $title .= 'Edit';
		
		// Return the finished title
		return $title;

	}
	
	// Save out an image or file given the field name.  They are saved
	// to the directory specified in the bundle config
	public function saveImage($input_name = 'image') { return $this->saveFile($input_name); }
	public function saveFile($input_name = 'file') {
		$path = File::organizeUploadedFile(Input::file($input_name), Config::get('decoy::upload_dir'));
		$path = File::publicPath($path);
		return $path;
	}
	
	// Many models will override this to create custom methods for getting
	// a list of rows
	static public function ordered() {
		return static::orderBy(self::tableName().'.created_at', 'desc');
	}
	
	// Get an ordered list of only rows that are marked as visible
	static public function orderedAndVisible() {
		return static::ordered()->where('visible', '=', '1');
	}
	
	//---------------------------------------------------------------------------
	// Utility methods
	//---------------------------------------------------------------------------
	
	// Randomize the results in the DB.  This shouldn't be used for large datasets
	// cause it's not very performant
	static public function randomize() {
		return static::orderBy(DB::raw('RAND()'));
	}
	
	// Find by the slug.  Like "find()" but use the slug column instead
	static public function findBySlug($slug) {
		return static::where(self::tableName().'.slug', '=', $slug)->first();
	}
	
	// Figure out the current table name but allow it to be called statically
	static protected function tableName() {
		$model = get_called_class();
		$model = new $model;
		return $model->getTable();
	}
	
	/**
	 * The pivot_id may be accessible at $this->pivot->id if the result was fetched
	 * through a relationship OR it may be named pivot_id out of convention (something
	 * currently done in Decoy_Base_Controller->get_index_child()).  This function
	 * checks for either
	 * @return integer 
	 */
	public function pivotId() {
		if (!empty($this->pivot->id)) return $this->pivot->id;
		else if (!empty($this->pivot_id)) return $this->pivot_id;
		else return null;
	}
	
	// Form a croppa URL, taking advantage of being able to set more columns null.  Also,
	// provides an easier way to inform the source crops
	public function croppa($width = null, $height = null, $crop_style = null, $field = 'image', $options = null) {
		
		// Check if the image field has crops
		if ($crop_style && !array_key_exists($field, static::$CROPS)) {
			throw new \Exception("A crop style was passed for $field but no crops are defined for that field.");
		}
		
		// Check if the crop style is valid
		if ($crop_style && !Collection::keyOrValueExists($crop_style, static::$CROPS[$field])) {
			throw new \Exception("Crop style '$crop_style' is not defined for the field: $field");
		}
		
		// Default crop style is 'default'
		if (!$crop_style && !empty(static::$CROPS[$field]) && Collection::keyOrValueExists('default', static::$CROPS[$field])) {
			$crop_style = 'default';
		}
		
		// Get the image src path
		if (method_exists($this, $field)) $src = call_user_func(array($this, $field));
		else $src = $this->$field;
		if (empty($src)) return;
		
		// If there is a crop value, add it to the options
		if ($crop_style) {
			$crops = json_decode($this->{$field.'_crops'});
			
			// Check if a crop style was set in the admin for this crop style
			if (!empty($crops->$crop_style)) {
				if (!is_array($options)) $options = array();
				
				// Add the trim instructions to the croppa options
				$options = array_merge($options, array(
					'trim_perc' => array(
						round($crops->$crop_style->x1,4),
						round($crops->$crop_style->y1,4),
						round($crops->$crop_style->x2,4),
						round($crops->$crop_style->y2,4),
					),
				));
			}
		}
		
		// Return the Croppa URL
		return Croppa::url($src, $width, $height, $options);
		
	}
	
	/**
	 * Get the admin controller class for this model.  It's assumed to NOT be a decoy controller.
	 * In other words, it's in app/controllers/admin/.
	 * @return string ex: Admin\ArticlesController
	 */
	static public function adminControllerClass() {
		return ucfirst(Config::get('decoy::dir')).'\\'.Str::plural(get_called_class()).'Controller';
	}
	
	/**
	 * Add a field to the blacklist
	 */
	public function blacklist($field) {
		$this->guarded[] = $field;
	}
	
}
{'fraction_non_alphanumeric': 0.19443305310641787, 'fraction_numerical': 0.002255484929259791, 'mean_word_length': 2.8870292887029287, 'pattern_counts': {'":': 0, '<': 9, '<?xml version=': 0, '>': 95, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6488924', 'n_tokens_mistral': 6109, 'n_tokens_neox': 5500, 'n_words': 1904}