20
votes

After instantiating a model class that extends Laravel's Eloquent Model class, is there a way to determine if the property/attribute maps to the table, and therefore can be saved to the table?

For example, I have a table announcements with columns id, text and "created_by".

How can I know created_by is an attribute and will be saved if set?

$announcement = new Announcement();

isset($announcement->created_by) understandably returns false if I haven't explicitly set the value yet. I have tried various functions inherited from the Eloquent model class, but so far none have worked. I'm looking for something like:

$announcement->doesThisMapToMyTable('created_by') that returns true whether or not $announcement->created_by has been set.

5
Mattcore, please consider setting @Leith simple answer as accepted. It is simple and short and will do what you asked forZanshin13

5 Answers

7
votes

For Laravel 5.4:

$hasCreatedAt = \Schema::hasColumn(app(Model::class)->getTable(), 'created_at');

    if($hasCreatedAt == true) {
       ...
    }
32
votes

If your model is filled with data, you can:

$announcement = Announcement::find(1);

$attributes = $announcement->getAttributes();

isset($attributes['created_by']);

For empty models (new Models), unfortunately you will have to get the columns using a small hack. Add this method to your BaseModel:

<?php

class BaseModel extends Eloquent {

    public function getAllColumnsNames()
    {
        switch (DB::connection()->getConfig('driver')) {
            case 'pgsql':
                $query = "SELECT column_name FROM information_schema.columns WHERE table_name = '".$this->getTable()."'";
                $column_name = 'column_name';
                $reverse = true;
                break;

            case 'mysql':
                $query = 'SHOW COLUMNS FROM '.$this->getTable();
                $column_name = 'Field';
                $reverse = false;
                break;

            case 'sqlsrv':
                $parts = explode('.', $this->getTable());
                $num = (count($parts) - 1);
                $table = $parts[$num];
                $query = "SELECT column_name FROM ".DB::connection()->getConfig('database').".INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = N'".$table."'";
                $column_name = 'column_name';
                $reverse = false;
                break;

            default: 
                $error = 'Database driver not supported: '.DB::connection()->getConfig('driver');
                throw new Exception($error);
                break;
        }

        $columns = array();

        foreach(DB::select($query) as $column)
        {
            $columns[$column->$column_name] = $column->$column_name; // setting the column name as key too
        }

        if($reverse)
        {
            $columns = array_reverse($columns);
        }

        return $columns;
    }

}

Extend your model from it:

class Announcement extends BaseModel {

}

Then you will be able to:

$columns = $announcement->getAllColumnsNames();

isset($columns['created_by']);
9
votes

Rather than writing your own lookup into the database internals, you can just use the Schema facade to look up the columns:

use Illuminate\Support\Facades\Schema;

$announcement = new Announcement();
$column_names = Schema::getColumnListing($announcement->getTable());
if (in_array('created_by', $column_names)) {
  // whatever you need
}

Alternatively, as early as Laravel 4 there's the hasColumn() check:

use Illuminate\Support\Facades\Schema;

$announcement = new Announcement();
if (Schema::hasColumn($announcement->getTable(), 'created_by')) {
  // whatever you need
}
0
votes

Laravel 5.4

I used Leiths answer to create generic helper method, which automatically maps JS-object to Laravel model (and basically ignores those properties which are not part of model, thus avoiding QueryBuilder error messages for undefined columns). Additionally contains simple createOrEdit -approach (even though doesn't have "does ID exist?" -validation):

    public function store(Request $request) {
        if($request->input("club")) {
            $club = \App\Club::find($request->input("club")["id"]);
        } else {
            $club = new \App\Club;
        }
        $club = self::mapModel($request->input("club"), $club);
        $club->save();
    }
    
    public function mapModel($input,$model) {
        $columns = Schema::getColumnListing($model->getTable());
        foreach ($columns as $i => $key) {
            if($input[$key]) {
                $model->{$key} = $input[$key];
            }
        }
        return $model;
    }
-1
votes

I used to !empty($announcement->created_by) and it's work great for my