Laravel 4 - is there a way to have a prefix for some tables, but not others? - laravel

Most of my database tables use a prefix set in the database config file, but I have several tables that are static that I use for lookups like States, Countries, etc. Is there a way to tell Laravel in the models or elesewhere that these tables do NOT use the prefix Right now my seeders and other features don't work because they assume the prefix.
I have the table names set in the models like this:
protected $table = 'states';
Of course I could either just make them use the prefix like all the other tables or create a separate database connection, but I'm wondering if there's another solution.
Thanks!

I was thinking about doing this for a project as well and I came upon this thread:
http://laravel.io/forum/03-20-2014-override-table-prefix-for-one-model
The last comment (as of right now at least) suggests setting up another db connection that uses an alternate (or no) prefix, then tell the specific models you want to use that connection.
eg
in the model:
protected $connection = 'db_no_prefix';
protected $table = 'this_table';
in db config
'connections' = > [
...
'db_no_prefix' => [
....
'prefix' => '',
],
],

There's no way Laravel (or any software in existence) would magically know which of your tables have prefixes and which ones don't if you don't define it somewhere. The place to define it is exactly where you said, in the model:
// State model
protected $table = 'states';
// Some other model
protected $table = 'pre_mytable';
If your models all clearly have their tables defined, your seeds should work perfectly.
Now, obviously if some piece of software had a list of database tables defined somewhere, it could iterate through them and determine which ones were prefixed. But this ultimately defeats the purpose since the original intention was for your software to figure out which ones were prefixed so it could know the table name to access.
Anyways, its a good practice to explicitly define your table names in the models. Someday down the road when you or someone else looks at your code, you might wonder what table a model is referring to, but if it's clearly defined then there is no way to be confused. Never try to rely on too much magic on an app if you want to understand things a year later.

I think part of the problem is in my seeders. I'm not using the Schema class to create the table, but am using it when dropping.
class CreateStatesTable extends Migration {
public function up()
{
DB::statement("
CREATE TABLE IF NOT EXISTS `".Config::get('database.connections.mysql.prefix')."_states` (
`state_id` tinyint(2) NOT NULL AUTO_INCREMENT,
`state_name` varchar(15) NOT NULL DEFAULT '',
`state_abbr` char(2) NOT NULL DEFAULT '',
PRIMARY KEY (`state_id`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=52 ;
");
}
public function down()
{
Schema::drop('states');
}
}
However, even if I solve this, I may still have problems in other parts of the system. Probably easier just to use the prefix and create duplicates of the tables in other apps.

Related

What is the best way to copy data from one field to another when creating a migration of a new field?

I have a database table having a field that has a boolean field type. Now, as per the new requirement, the field should be changed to the small integer type.
In order to achieve it, I created a migration and added the script in the same migration file to copy the value from the old field to the new field. However, I think this is not the best approach that I have followed. Can someone please help and advise about the best way to handle this scenario.
public function up()
{
Schema::table('skills', function (Blueprint $table) {
$table->tinyInteger('skill_type_id')->nullable()->comment = '1 for advisory skills, 2 for tools, 3 for language & framework';
});
$skill_object = (new \App\Model\Skill());
$skills = $skill_object->get();
if (_count($skills)) {
foreach($skills as $skill) {
$skill_type = 1;
if ($skill->is_tool) {
$skill_type = 2;
}
$skill_object->whereId($skill->id)->update(['skill_type_id' => $skill_type]);
}
}
}
You can do it with 02 migrations, the first one is to create the new field, as already did. The second is create a migration with raw statement to copy value from old field to new field.
If you don't need anymore old field, you can create a third migration deleting the old field.
public function up()
{
Schema::table('skills', function (Blueprint $table) {
DB::statement('UPDATE skills SET skill_type_id = IF(is_tool, 2, 1)');
}
}
You can do this(update the data) from the following way in your scenario.
Create separate routes and update the data after the migrations.
Create seeder(having the same query as above in migrations file) run the seeder.
But above both solutions are little risky if you are trying to do this with your production database. If someone mistakenly hit URL and run seeder multiple time, It's difficult to manage.
I believe the best way to solve your problem by seed(modify) the data on the same migrations file after modifying the schema because migrations won't run again (even mistakenly), Once it migrated.
You are doing the correct way as I believe.
You are free to develop your own way to achieve this task, but as far as migrations are concerned, these are meant for controlling and sharing the application's database schema among the team, not the actual data ;)
You can create separate seeder for this task.
It will keep your migration clean and easy to rollback if needed.
NOTE: Don't include this seeder class in DatabaseSeeder.
These kind of seeder class are only meant for update the existing data after fixing the current functionality(I am taking into consideration, you have already fixed the code as per your new requirement). So, there is not need to worry about re run the same seeder class.
Considering (laracast, stack-overflow), i will prefer to go by your way over the suggestions provided above as neither i have to maintain extra route nor additional migration (03).
The only improvement i can suggest here is you can use databse-transaction something like this :
// create new column
DB::transaction(function () {
update new column
delete old column
});

Laravel relations with composite, non-standard foreign keys

I unfortunately need to import data from a third-party vendor and use their non-standard database schema with my laravel project. In addition, I need to store multiple "firms," each with their own set of users in my database.
I'm trying to figure out the best way (if it can be done) to use Eloquent to handle the relationships between these tables. So for instance, with my table structure like this:
BmPerson
'id',
'firmId',
'personId'
BmCoverage
'id',
'firmId',
'personId',
'securityId'
BmSecurity
'id',
'firmId',
'securityId'
... for instance, I need to associate a "BmPerson" with many "BmSecurity" through the "BmCoverage" table.
But I need to somehow use composite keys, because I am storing multiple "firms" in each table (per the 3rd party vendor's database schema).
One approach I've used so far is scoping, e.g.: for my BmCoverage model:
public function scopeFromFirm($query,$firmId){
return $query->where('firmId','=',$firmId);//->where('personId','=',$personId);}
public function scopeFromPerson($query,$personId){
return $query->where('personId','=',$personId);//->where('personId','=',$personId);}
Then I can retrieve the coverage list for an individual person, but I still need to somehow be able to associate the "BmCoverage" with the "BmSecurities." I suppose I could just add a scope the BmSecurities class too, but it would be nicer to just use Eloquent.
Has anyone come up with a good way to use composite keys in laravel model relationships, or should I just stick with the scoping method?
There is a package here that seems to be perfect for your case:
Compoships offers the ability to specify relationships based on two
(or more) columns in Laravel 5's Eloquent. The need to match multiple
columns in the definition of an Eloquent relationship often arises
when working with third party or pre existing schema/database.
You would use it like this:
class BmPerson extends Model
{
use \Awobaz\Compoships\Compoships;
public function bmCoverages()
{
return $this->hasMany('App\BmCoverage', ['firmId', 'personId'], ['firmId', 'personId']);
}
}
If every BmSecurity belongs to exactly one BmCoverage, and every BmCoverage belongs to exactly one BmPerson its probably easier to replace 'firmId', 'personId' with bmperson_id in BmCoverage DB; and 'firmId', 'securityId' with bmcoverage_id in BmSecurity. Then you can use default hasMany relations with one key.
Everything you need for this can be found here https://laravel.com/docs/5.2/eloquent-relationships
You can easily define which cols sohuld be the referenced key.
Example:
public function bmCoverages() {
return $this->hasMany('App\BmCoverage', 'firmId', 'id');
}
This would probably belong to your App\Firm or whatever it is called.
In general the hasMany relations looks like this
return $this->hasMany('App\Comment', 'foreign_key', 'local_key');
As you can see you can specify the keys.
As the others have said, you need to use the HasMany and HasManyThrough relationship.
Here from your table definitions, you simply need access to:
Person->BmCoverage(s)
Person->BmSecurity(s) of an individual.
What I think is the major problem here is linking the BmSecurity with BmCoverage as apparently there's no coverage_id per BmSecurity but rather, a composite mapping through firmId and securityId.
In this case, Laravel does not officially support composite keys unfortunately, although you could use a trait like this... but you could also achieve the same with some tricky hasMany.
i.e. on BmCoverage
$this->hasMany('BmSecurity', 'securityId', 'securityId')
->andWhere('firmId', '=', $this->firmId);
Same applies for BmSecurity from BmPerson using HasManyThrough.
Hope that helps.
read laravel hasManyThrough relationship . it will help you to write this query more easily
https://laravel.com/docs/5.1/eloquent-relationships#has-many-through

Laravel5: How are Eloquent model relationships expressed in the database?

There's a missing link I fail to understand.
I use migrations to create database tables and I define the relationships there. meaning.. if I have a person table and a job table and I need a one to many relationship between the person and jobs, I'd have the job table contain a "person_id".
When I seed data or add it in my app, I do all the work of adding the records setting the *_id = values etc.
but somehow I feel Laravel has a better way of doing this.
if I define that one to many relationship with the oneToMany Laravel Eloquent suports:
in my Person model.....
public function jobs()
{
return $this->hasMany('Jobs);
}
what's done on the database level? how do I create the migration for such table? Is Laravel automagically doing the "expected" thing here? like looking for a Jobs table, and having a "person_id" there?
Yep, Laravel is doing what you guess in your last paragraph.
From the Laravel documentation for Eloquent Relationships (with the relevant paragraph in bold):
For example, a User model might have one Phone. We can define this
relation in Eloquent:
class User extends Model {
public function phone()
{
return $this->hasOne('App\Phone');
}
}
The first argument passed to the hasOne method is the name of the
related model. Once the relationship is defined, we may retrieve it
using Eloquent's dynamic properties:
$phone = User::find(1)->phone;
The SQL performed by this statement
will be as follows:
select * from users where id = 1
select * from phones where user_id = 1
Take note that Eloquent assumes the foreign key of the relationship based on the model name. In this case, Phone model is assumed to use a user_id foreign key.
Also note that you don't actually have to explicitly set the foreign key indexes in your database (just having those "foreign key" columns with the same data type as the parent key columns is enough for Laravel to accept the relationship), although you should probably have those indexes for the sake of database integrity.
There is indeed support to create foreign key relationships inside migration blueprints and it's very simple too.
Here is a simple example migration where we define a jobs table that has a user_id column that references the id column on users table.
Schema::create('jobs', function($table)
{
$table->increments('id');
$table->integer('user_id')->unsigned();
$table->foreign('user_id')->references('id')->on('users');
});
You can also use some other methods that laravel provides such as onDelete() or onUpdate
Of course to understand better the options that are available to you please read the documentation here.
Edit:
Keep in mind that Eloquent is just using fluent SQL wrapper and behind the scenes there are just raw sql queries, nothing magical is happening, fluent just makes your life a lot easier and helpers you write maintainable code.
Take a look here about the Query Builder and how it works and also, as #Martin Charchar stated , here about Eloquent and relationships.

Can eloquent ignore irrelevant data in Laravel 4

I have a form that accepts data which will be used to create two new database table entries. The form takes both the users details and their address. The user details will be stored using the User::create(Input::all()) method into the users table, and the address details will be stored using the Address::create(Input::all()) method into the addresses table of the database.
The issue I'm currently having is that Eloquent is complaining that street, city, country etc do not exist on the users table. This is true, that data is to be used for the address side of things.
Is there any way to have eloquent ignore irrelevant data in the Input::all() array when it's passed to the create methods?
P.s. I'm aware that mass-assignment isn't a good idea, I'm only using it here to simplify my question.
Sure enough you can use $fillable array in your model to declare fields allowed for mass-assignment. I believe this is the most sufficient solution in your case.
class User extends Eloquent {
protected $fillable = [
'first_name',
'last_name',
'email'
];
}
Have you tried looking at Input::only('field1','field2',...);, or even Input::except('field3')? They should be able to accomplish what you are looking for.
Source: http://laravel.com/docs/requests
You'll have to unguard that model using these http://laravel.com/docs/eloquent#mass-assignment and then manually unset those values before you execute save(). I highly recommend using a form object or something similar to complete this kind of service for you outside of your model since it's safer and usually clearer to intended behavior.
#cheelahim is correct, When passing an array to Model::create(), all extra values that aren't in Model::fillable will be ignored.
I would however, STRONGLY RECOMMEND that you do not pass Input::all() to a model. You really should be validating and verifying the data before throwing it into a model.

Adding new columns to an Existing Doctrine Model

First of all Hats of to StackOverflow for their great service and to you guys for taking your time to answer our questions.
I am using Doctrine ORM 1.2.4 with CodeIgniter 1.7.3. I created a Site with some required tables and pumped in with datas only to realize at a later point of time that a specific table needs to have one more column.
The way i created the tables was by writing the model as php classes which extend the Doctrine_Record.
Now i am wondering if i need to just add the column in the model that requires a new column in the setTableDefinition() method and recreate that table or is there any other way that easily does this. The former method i've mentioned requires me to drop the current table along with the datas and recreate the table which i do not wish. Since doctrine seems to be a very well architect-ed database framework, i believe it is lack of my knowledge but surely should exist a way to add new columns easily.
PS: I am not trying to alter a column with relations to other tables, but just add a new column which is not related to any other table. Also i create the tables in the database using Doctrine::createTablesFromModels(); When i alter a table with a new column and run this method it shows errors.
Since you don't want to drop & recreate, use a Doctrine Migration.
The official docs here show many examples:
http://www.doctrine-project.org/projects/orm/1.2/docs/manual/migrations/en
Since you just want to add a field, look at their second code example as being the most relevant which is like this:
// migrations/2_add_column.php
class AddColumn extends Doctrine_Migration_Base
{
public function up()
{
$this->addColumn('migration_test', 'field2', 'string');
}
public function down()
{
$this->removeColumn('migration_test', 'field2');
}
}

Resources