I'm converting a database from being managed by SQL dumps to being managed by schemas and migrations. Part of this is seeding data. I've based what I'm doing from the schema example on CakePhp's page about Schemas.
The weird thing is that the first table to be seeded with data works without problem, and the second fails with an error like Table users for model User was not found in datasource default. This happens even if I change which table will be seeded: the first one succeeds (and I've checked in the database that the data is there) and the next one to be seeded fails.
I've also checked the error message against the database, and every table it complains about not existing does actually exist.
My 'schema.php' looks like this:
class AppSchema extends CakeSchema {
public function before($event = array()) {
return true;
}
private function create_many($class_name, $entries){
App::uses('ClassRegistry', 'Utility');
$class = ClassRegistry::init($class_name);
foreach($entries as $entry){
$class->create();
$class->save(array($class_name => $entry));
}
}
private function create_many_kv($class_name, $keys, $values_matrix){
$entries = array();
foreach($values_matrix as $values){
array_push($entries, array_combine($keys, $values));
}
$this->create_many($class_name, $entries);
}
public function after($event = array()) {
if (isset($event['create'])) {
switch ($event['create']) {
case 'users':
$this->create_many('User', array(
array('emailaddress' => 'email',
'password' => 'hash',
'role_id' => 1
),
array('emailaddress' => 'email2',
'password' => 'hash',
'role_id' => 3)
));
break;
case 'other_table':
$this->create_many('OtherTable', array(
array('id' => 1,
'name' => 'datum'),
array('id' => 2,
'name' => 'datum2')
));
break;
etc.
The answer for me here was to populate all of the tables after the last table has been created.
My best hypothesis for why this didn't work as described in the question is that Cake is caching the database structure in memory, and this isn't updated when the new tables are added. I can't find any documentation about clearing that structure cache so a workaround is the closest thing to a solution for now.
When inserting data to more than one table you’ll need to flush the database cache after each table is created. Cache can be disabled by setting $db->cacheSources = false in the before action().
public $connection = 'default';
public function before($event = array()) {
$db = ConnectionManager::getDataSource($this->connection);
$db->cacheSources = false;
return true;
}
Related
if you ever encountered a problem, when you cannot issue a sluggable behavior on a translated field, I feel ya.
Whenever you save a translation for an entity, 'slug' property is omitted because it's not dirty in the time of saving the translation entity.
You save an entity.
Translations are being created.
The table for i18n has no sluggable behavior attached, so it does not know, when to issue a sluggable behavior on a translated field like title / name etc.
I think I've found a better solution:
In my SluggableBehavior class, I've updated the behavior to include translations too:
public function beforeSave(Event $event, EntityInterface $entity) {
$this->slug($entity);
if($entity->get('_translations')) {
foreach($entity->get('_translations') as $key=>$translation) {
$this->slug($translation);
}
}
}
Of course, simply as it can be, it does not need a separate table :-) But thanks #ndm.
You can create and use a concrete table class for the translation table, where you can then create the slugs.
By default the name that the translate behavior uses for looking up table classes is I18n, so if you want this to apply to all translated tables, create App\Model\Table\I18nTable, or if you want this to apply to specific translated tables only, create a separate database translation table and class, and configure the translate behavior accordingly via the translationTable option:
// looks up `App\Model\Table\CustomI18nTable`
'translationTable' => 'CustomI18n'
See also
Cookbook > Database Access & ORM > Behaviors > Translate > Using a Separate Translations Table
Solution I think of might be and it's tested:
you specify a protected property in your entity, like:
protected $_sluggable = 'title';
then you create a getter:
public function _getSluggableField() {
return $this->_sluggable;
}
as soon as you do that you need to update the vendor file:
vendor/cakephp/cakephp/src/ORM/Behavior/TranslateBehavior.php
and change:
foreach ($translations as $lang => $translation) {
foreach ($fields as $field) {
if (!$translation->isDirty($field)) {
continue;
}
$find[] = ['locale' => $lang, 'field' => $field, 'foreign_key' => $key];
$contents[] = new Entity(['content' => $translation->get($field)], [
'useSetters' => false
]);
}
}
to:
foreach ($translations as $lang => $translation) {
foreach ($fields as $field) {
if($field==='slug' && (method_exists($entity, '_getSluggableField') && $entity->_getSluggableField())) {
$translation->set('slug', \Cake\Utility\Text::slug($translation->get($entity->_getSluggableField())));
}
if (!$translation->isDirty($field)) {
continue;
}
$find[] = ['locale' => $lang, 'field' => $field, 'foreign_key' => $key];
$contents[] = new Entity(['content' => $translation->get($field)], [
'useSetters' => false
]);
}
}
I hope someone has a better solution. But this one works as a charm.
I am very much new to laravel framework.
I have one form , which i need to update on submit button click.
when submit button clicks control goes to controller.php 's update() function .
But I am unable to edit any field's value.
here is my code.
public function update($id)
{
//echo "<pre>";print_r(Input::all());exit;
$product = $this->product->find($id);
$input = Input::only('designer', 'sku', 'name', 'display_name', 'description', 'price', 'main_category', 'sub_category', 'lead_time', 'sizing', 'woven', 'body_fabric', 'lining_fabric', 'fit', 'primary_color', 'secondary_color', 'care_label', 'neck_type', 'closure', 'trims', 'special_finishings', 'image1', 'image2', 'image3', 'image4', 'image5','top', 'combo_products', 'keywords', 'visibility', 'featured');
//echo "<pre>";print_r($input);exit;
try
{
$this->adminNewProductForm->validate($input);
} catch(\Laracasts\Validation\FormValidationException $e)
{
return Redirect::back()->withInput()->withErrors($e->getErrors());
}
$slug = Str::slug(Input::get('name'));
$slug = $this->product->getSlug($slug);
$input = array_add($input, 'slug', $slug);
DB::transaction(function() use($product, $input)
{
$product->fill($input)->save();
$stock_count = 0;
if(!empty(Input::get('xsmall_size')))
{
$rows = DB::table('products_variants')->where('product_id', $product->id)->where('variant_name', 'XS')->get();
$stock_count += Input::get('xsmall_stock');
if(!empty($rows))
{
DB::table('products_variants')->where('product_id', $product->id)->where('variant_name', 'XS')->update(array('variant_specs' => Input::get('xsmall_size'), 'price_change' => Input::get('xsmall_price'), 'total_stock' => Input::get('xsmall_stock'), 'stock_used' => 0));
} else {
DB::table('products_variants')->insert(array('product_id' => $product->id, 'variant_name' => 'XS', 'variant_specs' => Input::get('xsmall_size'), 'price_change' => Input::get('xsmall_price'), 'total_stock' => Input::get('xsmall_stock'), 'stock_used' => 0));
}
}
$input = array();
$input['flagship_status'] = Input::get('flagship_status');
if(Input::get('flagship_status'))
{
$input['stock_count'] = Input::get('small_stock');
}else {
$input['stock_count'] = $stock_count;
}
$product->fill($input)->save();
});
//echo "<pre>";print_r(Input::all());exit;
return Redirect::back()->withFlashMessage('Product Updated Successfully!');
}
Also I cant understand , what is going on by this line ? because i did not find validate function anywhere in my code.
$this->adminNewProductForm->validate($input);
I need to update table products not products_variants.
validate is inherited from the FormRequst class.
https://laravel.com/api/5.0/Illuminate/Foundation/Http/FormRequest.html#method_validate
You've provided too much code and too little information. You said you need to update a specific table, but yet there are two lines where you are very intentionally manually updating a database entry.
This is one of them:
DB::table('products_variants')->where('product_id', $product->id)->where('variant_name', 'XS')->update(array('variant_specs' => Input::get('xsmall_size'), 'price_change' => Input::get('xsmall_price'), 'total_stock' => Input::get('xsmall_stock'), 'stock_used' => 0));
When you call this:
$product->fill($input)->save();
It also saves 'dirty' (modified) models that also belong to it, which can include products_variants relationships. From the sound of it, you are incorrectly applying changes directly through SQL, and then the model's save method is overwriting it.
You seem unclear about what your code is actually doing, and I would strongly suggest simplifying it down and adding in code as you begin to understand what each line does. I think your question is the byproduct of copying an example and adding your own work without understanding how Laravel handles relationships and models. There is almost never a good reason to use raw SQL or DB statements.
I am working on my first project using Laravel 5.1. Uses a selectbox in a form.
{!!Form::select('animal_parent[]', array('1' => 'opt1', '2' => 'opt2', '3' => 'opt3', '4' => 'opt4',), null, ['id' => 'animal_parent', 'disabled' => 'disabled', 'multiple' => 'multiple', 'class' => 'form-control'])!!}
Selection limited to two options which need to saved in two columns, male_parent and female_ parent of the animal table.
There are no male_parent and female_ parent element names in the form. Similarly no animal_parent field in animal table.
Values are set as expected in the code given below. However, the insert command does not reflect the newly set values and throws an error.
"ErrorException in helpers.php line 671: preg_replace(): Parameter mismatch, pattern is a string while replacement is an array."
Any help would be much appreciated.
First attempt using mutators
public function setMaleParentAttribute()
{
$parent = Input::get('animal_parent');
$this->attributes['male_parent'] = intval($parent[0]);
}
public function setFemaleParentAttribute(AddAnimalRequest $request)
{
$parent = Input::get('animal_parent);
if (isset($parent[1])) {
$this->attributes['female_parent'] = intval($parent[1]);
} else {
$this->attributes['female_parent'] = intval($parent[0]);
}
unset($request->animal_parent);
}
Second attempt using the store() method in the controller.
$animal = new Animal($request->all());
$parent = Input::get('animal_parent');
$animal['male_parent'] = intval($parent[0]);
if (isset($parent[1])) {
$animal['female_parent'] = intval($parent[1]);
} else {
$animal['female_parent'] = intval($parent[0]);
}
unset($request->animal_parent);
Auth::user()->animals()->save($animal);
return redirect('animals');
The problem was then solved with a change in UI. I feel the problem could have been solved using the below method. Hope that helps someone.
$input = $request->all();
$parent = $input['animal_parent'];
$input['male_parent'] = intval($parent[0]);
if (isset($parent[1])) {
$input['female_parent'] = intval($parent[1]);
} else {
$input['female_parent'] = intval($parent[0]);
}
unset($input['animal_parent']);
$animal = new Animal($input);
$animal->save();`
Problem: I want to get Customers only if they have already placed an order for the current year. Customers and Orders are on two separate Databases (this can't change). The relationships are all setup and working correctly but when I try the following I keep getting an SQL error as it is trying to search 'orders' on the 'customers' database. Is there anyway to force Laravel to use the correct database in this scenario?
$customers = $customers->whereHas('orders', function($query){
$query->where('academic_year_id', '=', $this->current_academic_year->id);
});
$customers = $customers->orderBy('DFKEY','ASC')->get();
Order Model:
public function customer()
{
return $this->belongsTo('Customer','dfkey');
}
Customer Model:
protected $connection = 'mysql2';
public function orders()
{
return $this->hasMany('Order','dfkey','DFKEY');
}
Thanks in advance!
Late to the party but for anybody else who has a similar issue, the below should work (as long as both databases are on a single server).
Set BOTH the connection and table explicitly.
protected $connection = 'mysql2';
protected $table_name = 'mysql2.orders';
Or if you want - dynamically set the table like this:
protected $table = 'orders';
public function __construct() {
$this->table = DB::connection($this->connection)->getDatabaseName() . '.' . $this->table_name;
}
Or even
public function __construct() {
$this->table = DB::connection($this->connection)->getDatabaseName() . '.' . $this->getTable();
}
Solved this by using a filter:
public function index()
{
$customers = new Customer;
// Make sure customers are current parents of students
$customers = $customers->whereHas('students', function($q) {
$q->where('STATUS', '=', 'FULL');
});
//Filter results
if(Input::get('academic_year') == 'ordered'){
$customers = $customers->orderBy('DFKEY','ASC')->get();
$filtered = $customers->filter(function($customer)
{
if ($customer->orders()->where('academic_year_id','=',$this->current_academic_year->id)->first()) {
return true;
}
});
$customers = $filtered;
return View::make('admin.customers.index',compact('customers'));
}
$customers = $customers->orderBy('DFKEY','ASC')->get();
return View::make('admin.customers.index',compact('customers'));
}
This is actually a very good question!
Simple queries directly on that model won't be a problem because Laravel uses the default connection from the model, but if you have a related model in another database and you execute some advanced queries, like: whereHas('relationName'), Laravel will use the connection of parent which will cause the SQL error about non existing column (because it looks up in wrong database).
This is the best solution for this problem:
In your related model make a constructor like this:
public function __construct(array $attributes = [])
{
$this->table = 'db_name.'.$this->table;
parent::__construct();
}
Unfurtanelly there is no direct way, but you can do it in 2 steps, which is kind of the same way that Laravel does it normally.
Its simple, clean, and efective, also you dont mess with core Laravel functions that may change on an update
$orders = Order::where('academic_year_id', $this->current_academic_year->id)
->select('id')
->pluck('id');
$customers = $customers->whereIn('order_id', $orders)
->orderBy('DFKEY','ASC')
->get();
Hope this works for you.
P.D.: you can skip the equal sign in where
Package hoyvoy/laravel-cross-database-subqueries allows you to do that for databases in the same server. You also need to specify all database connections and models related to them as specified by Agus Trombotto. Finally the model refering to another database must extend from Hoyvoy\CrossDatabase\Eloquent\Model instead of Illuminate\Database\Eloquent\Model.
Try to write query like this and put your database and tablename as it is->
$customers = Schema::connection('your_database_name')::table('respective_table_name')
->where('academic_year_id', '=', $this->current_academic_year->id)
->orderBy('DFKEY','ASC')
->get();
I 've solved this by adding 'connection' and 'table' variables models specificing the database in wich the model is saved.
For example, i have a model called 'User' in the database called 'core_database' in the table users.
In the other side, i have a model called 'UserEvents' in the database called 'logs_database' in the table 'user_events'
So i will have a two conections on config/database.php file:
'core_db_connection' => [
'driver' => 'mysql',
'host' => host_ip,
'port' => port,
'database' => 'core_database',
'username' => username,
'password' => password,
....
],
'logs_db_connection' => [
'driver' => 'mysql',
'host' => host_ip,
'port' => port,
'database' => 'logs_database',
'username' => username,
'password' => password,
....
],
And the models will be like:
class User extends Authenticatable {
protected $table = 'core_database.users';
protected $connection = 'core_db_connection';
...
}
class UserEvents extends Model {
protected $table = 'logs_database.user_events';
protected $connection = 'logs_db_connection';
...
}
This was tested in databases in the same database server.
Database connections have the same host ip.
I have not tried with a different way
Using this configuration, i can make any query across two or more separeted database in the same database server, in my case, RDS.
I hope this help you!
I am creating a basic CMS to teach myself the fundamentals of Laravel and PHP.
I have a 'pages' table and I am storing a url_title. I want this URL title to be unique for obvious reasons. However, whatever I do to validate it, fails. It just saves anyway. I'm sure it is something simple. Can you spot what is wrong with this code?
I am also using Former in the view, that doesn't validate either. I have tried hard-coding a value as the last option in the unique method and it fails also.
http://anahkiasen.github.io/former/
http://laravel.com/docs/validation#rule-unique
States: unique:table,column,except,idColumn
Here is my Controller:
public function store()
{
$validation = Pages::validate(Input::all());
if($validation->fails()) {
Former::withErrors($validation);
return View::make('myview');
} else {
Pages::create(array(
'title' => Input::get('title'),
'url_title' => Input::get('url_title'),
'status' => Input::get('status'),
'body' => Input::get('body'),
'seo_title' => Input::get('seo_title'),
'seo_description' => Input::get('seo_description')
));
//check which submit was clicked on
if(Input::get('save')) {
return Redirect::route('admin_pages')->with('message', 'Woo-hoo! page was created successfully!')->with('message_status', 'success');
}
elseif(Input::get('continue')) {
$id = $page->id;
return Redirect::route('admin_pages_edit', $id)->with('message', 'Woo-hoo! page was created successfully!')->with('message_status', 'success');
}
}
}
Here is my model:
class Pages extends Eloquent {
protected $guarded = array('id');
public static $rules = array(
'id' => 'unique:pages,url_title,{{$id}}'
);
public static function validate($data) {
return Validator::make($data, static::$rules);
}
}
I have tried the following:
public static $rules = array(
// 'id'=> 'unique:pages,url_title,{{$id}}'
// 'id'=> 'unique:pages,url_title,$id'
// 'id'=> 'unique:pages,url_title,:id'
// 'id'=> 'unique:pages,url_title,'. {{$id}}
// 'id'=> 'unique:pages,url_title,'. $id
);
Any ideas? I spoke to the guy who created Former. He can't make head nor tail about it either. He suggested tracking it back to find our what query Laravel uses to check the uniqueness and try running that directly in my DB to see what happens. I can't find the query to do this. Does anyone know where to track it down?
Many thanks
Your rule should be:
public static $rules = array(
'url_title' => 'unique:pages,url_title,{{$id}}'
);
As I guessed from your code Input::get('url_title')
You have to use the field name used in the form.
Thanks peeps. I have been using the Laravel unique solutions and it hasn't been working well. I found this package which solves the issue brilliantly.
https://github.com/cviebrock/eloquent-sluggable#eloquent
Definitely worth a look.
Thanks for your feedback.