Is possible to remove a column in a migration postUp() method or is only intended to be used for data manipulation?
I don't know what version of Doctrine Migrations you are using. However, I just ran across this same question when working with Doctrine Migrations 2.0 and started digging into the code. When looking at how a Version is constructed, it appears that using the Schema object to make changes in the postUp() method does not take effect.
However, upon further consideration, this really makes sense. Each migration version is intended to modify the database structure. This takes place in the up() and down() methods of each migration. postUp() seems to be intended mostly for post structural changes cleanup (i.e. manipulating data). Any further structural modifications that you had intended on making in the postUp() method are should be made in a subsequent migration file.
For example, I was attempting to create a new table that was going to hold two columns from a previous table. I intended to drop those columns from the previous table after I had migrated data over to the new table. The code follows:
class Version20110512223208 extends AbstractMigration
{
protected $customerRepository;
protected $evernoteRepository;
public function up(Schema $schema)
{
$table = $schema->createTable('customer_evernote');
$table->addOption('type', 'INNODB');
$table->addOption('charset', 'utf8');
$table->addOption('collate', 'utf8_unicode_ci');
// Columns.
$table->addColumn('customer_id', 'bigint', array(
'length' => 20,
'notnull' => true,
'autoincrement' => false));
$table->addColumn('integration_date', 'datetime', array('notnull' => true));
$table->addColumn('oauth_token', 'string', array(
'length' => 255,
'notnull' => true));
$table->addColumn('oauth_shard_id', 'string', array(
'length' => 4,
'notnull' => true,
'fixed' => true));
$table->setPrimaryKey(array('customer_id'), 'pk_customer_id');
$table->addForeignKeyConstraint($schema->getTable('customer'), array('customer_id'), array('id'));
}
public function down(Schema $schema)
{
$schema->dropTable('customer_evernote');
}
public function preUp(Schema $schema)
{
$this->addSql("ALTER TABLE `customer` ENGINE = INNODB");
}
public function postUp(Schema $schema)
{
$this->skipIf($this->version->isMigrated() !== true, 'postUp can only apply if migration completes.');
// Copy the data from the customer table into the newly created customer_evernote table.
$this->doctrine = \Zend_Registry::get('doctrine');
$this->entityManager = $this->doctrine->getEntityManager();
$this->customerRepository = $this->entityManager->getRepository('My\Entity\Customer');
$this->evernoteRepository = $this->entityManager->getRepository('My\Entity\CustomerEvernote');
$customers = $this->customerRepository->findAll();
foreach ($customers as $customer)
{
$evernoteRecord = new \My\Entity\CustomerEvernote();
$evernoteRecord->setCustomerId($customer->getId());
$evernoteRecord->setCustomer($customer);
$evernoteRecord->setOauthToken($customer->getEvernoteOauthToken());
$evernoteRecord->setOauthShardId($customer->getEvernoteOauthShardId());
$evernoteRecord->setIntegrationDate(new \DateTime("now"));
$this->evernoteRepository->saveEvernote($evernoteRecord);
}
// Drop the columns from the existing customer table.
$table = $schema->getTable('customer');
$table->dropColumn('evernote_oauth_token');
$table->dropColumn('evernote_oauth_shard_id');
}
public function preDown(Schema $schema)
{
// Create the existing columns in the customer table.
$table = $schema->getTable('customer');
$table->addColumn('evernote_oauth_token', 'string', array(
'length' => 255,
'notnull' => false));
$table->addColumn('evernote_oauth_shard_id', 'string', array(
'length' => 4,
'notnull' => false,
'fixed' => true));
// Copy the data to the customer table.
$this->doctrine = \Zend_Registry::get('doctrine');
$this->entityManager = $this->doctrine->getEntityManager();
$this->customerRepository = $this->entityManager->getRepository('My\Entity\Customer');
$this->evernoteRepository = $this->entityManager->getRepository('My\Entity\CustomerEvernote');
$integrations = $this->evernoteRepository->findAll();
foreach ($integrations as $integration)
{
$integration->getCustomer()->setEvernoteOauthToken($integration->getOauthToken());
$integration->getCustomer()->setEvernoteOauthShardId($integration->getOauthShardId());
$this->customerRepository->saveCustomer($integration->getCustomer());
}
}
}
In reality, if I were to move the code at the end of postUp() to a new version's up() and the code at the beginning of the preDown() to a new version's down() method, I get the same results as in the class above, just performed in two separate steps. With this approach, I guarantee that I have structural modifications strictly taking place in my up and down methods.
Related
my question has two parts
Firstly, My if statement is not working. My if statement is as followed:
if ($request->is_published) {
$resources_page->published_at = now();
}
This is stored in my controller, I have a model for this and it is as followed:
public function is_published()
{
return $this->published_at !== null;
}
It is meant to check whether my checkbox is checked and return the timestamp, I have it cast in my model like followed:
protected $casts = [
'published_at' => 'datetime',
];
And in my view:
#include('components.form.input-checkbox', [
'label' => 'Publish?',
'form_object' => 'page',
'name' => 'is_published'
])
Could anyone elude to the answer?
Secondly, when trying to sync, it is not storing to my resources_category_resources_page table
In my controller, i have the following code
$resources_page->resources_categories()->sync(
ResourcesCategory::whereIn('slug', $request->resources_categories)->pluck('id')
);
In my model I have the relationships declared properly, so I don't know why its not storing?
I'm trying to store an image using Backpack's CRUD.
The Model's name is ProductModel, and in the SetupCreateOperation, I have:
CRUD::addField([
'name' => 'photo',
'label' => 'Foto',
'type' => 'image'
]);
When I try to upload an image, I get an error saying the following.
String data, right truncated: 1406 Data too long for column 'photo.'
Indeed the string being passed is almost 7000 characters long.
Model
class ProductModel extends Model
{
use \Backpack\CRUD\app\Models\Traits\CrudTrait;
use HasFactory;
protected $guarded = [];
public function products()
{
return $this->hasMany('App\Models\SoldProduct',
'product_model_id', 'id');
}
}
Migration
Schema::create('product_models', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->string('photo'); //path
$table->integer('stock');
$table->integer('limited_edition_pieces');
$table->decimal('price', 8, 2);
$table->string('note')->nullable();
$table->timestamps();
});
What should I do?
In order to set Image you need to add Field like this
$this->crud->addField([
'name' => 'image',
'label' => 'Image',
'type' => 'upload',
'upload' => true
]);
use 'disk' => 'uploads' if you want to upload to s3(amazon), otherwise don't add it if you want to keep images in public folder.
Also keep in mind that your image attribute needs to be set in your model.
Like this,
public function setImageAttribute($value)
{
$attribute_name = "image";
// you can check here if file is recieved or not using hasFile()
$disk = "public";
$destination_path = "/uploads";
$this->uploadFileToDisk($value, $attribute_name, $disk, $destination_path);
}
The uploadFileToDisk() lies in your Backpack\CRUD\app\Models\Traits\CrudTrait which you have already added
I haven't been able to find a solution that solves this. I am trying to query my database for a list of groups to which a user belongs. Users can be in multiple groups, and groups can have multiple users.
I have "users", "groups", and "groups_users" tables. Each has an "id" field, and the "groups_users" table has two other fields, "group_id" and "user_id".
I think all the tables are properly formed.
My User.php, and Group.php model files look like this
//app/Model/User.php
class User extends AppModel {
public $recursive = 1;
public #hasAndBelongsToMany = array(
'Group' => array(
'className' => 'Group',
'joinTable' => 'groups_users',
'foreignKey' => 'user_id',
'associationForeignKey' => 'group_id'
)
);
}
//app/Model/Group.php
class Group extends AppModel {
public $recursive = 1;
public #hasAndBelongsToMany = array(
'User' => array(
'className' => 'User',
'joinTable' => 'groups_users',
'foreignKey' => 'group_id',
'associationForeignKey' => 'user_id'
)
);
}
Then, in my Controller file I have this
//app/Controller/ProjectController.php
class ProjectController extends AppController {
public $uses = array('Groups', 'Users');
...
$this->set('groupsForUser', $this->Groups->find('all', array(
'conditions' => array('Users.UserName' => 'testuser1')
));
}
Every time I try to display the data, I get the error message
Error: ..... Unknown column 'Users.UserName' in 'where clause'
And it shows me the SQL it's trying to run:
SELECT Groups.id, ... FROM accounts.groups AS Groups WHERE Users.UserName = 'testuser1'
Obviously the association between the tables isn't happening properly and the SQL query it is sending in isn't joined properly, but I can't figure out what's going wrong here. I've tried varieties like
.....$this->Groups->Users->find(....
And stuff like that, but nothing seems to work.
Help!
I think You should use "User.UserName" instead of "Users.UserName" in your ProjectController.php because you have "User" in your associations So model name should be same.
//app/Controller/ProjectController.php
class ProjectController extends AppController {
public $uses = array('Groups', 'User');
...
$this->set('groupsForUser', $this->Groups->find('all', array(
'conditions' => array('User.UserName' => 'testuser1')
));
}
I had to take a completely different approach to the problem. It's mostly "Cake-y".
I simplified the models a bit by taking out the $recursive bit and trimmed down the HABTM part, removing the joinTable, foreignKey, and associationForeignKey bits)
The ProjectController got the code:
public $uses = array('Group', 'User');
$arrayGroupsForUser = array();
....
$results = $this->User->Find('first', array(
'conditions' => array('User.UserName' => 'testuser1')
));
foreach($results['Group'] as $result) {
array_push($arrayGroupsForUser, $result['name']);
}
$this->set('groupsForUser', $arrayGroupsForUser);
(Thank you ned stark for noting the need for a singular "User". I can't upvote your answer with my limited reputation yet.)
The "$this->set(.....);" line is to pass just the array of group names to the View to use.
This link http://rottmann.net/2012/07/cakephp-hasandbelongstomany-relationship-queries-demystified/ was a big help in helping me solve this.
When designing a relationship in Datamapper one is bound to call the relationship the same name as the related object, which is not too handy when you have something like Application_Model_User as a class name. For those of you who will rush to say that there is a configuration option with "class" key, I know. Been there tried that. It only works for getting a related object, not for updating them.
Here is a code snippet to reproduce the problem:
// User Model
class UserModel extends Datamapper
{
public $table = 'users';
public $has_many = array(
'roles' => array(
'class' => 'RoleModel',
'other_field' => 'usermodel',
'join_other_as' => 'role',
'join_self_as' => 'user',
'join_table' => 'users_roles'
),
);
}
class RoleModel extends DataMapper
{
public $table = 'roles';
public $has_many = array(
'usermodel' => array('class' => 'UserModel',
'other_field' => 'roles',
'join_other_as'=> 'user',
'join_self_as' => 'role',
'join_table' => 'users_roles' )
);
}
// controller code. Make sure you have a role with INT id = 2, and a user with INT id = 5 in your db
$user = new UserModel(2);
$role = new RoleModel(5);
$user->save($role);
This code gives an "Unable to relate usermodel with rolemodel." error, however it does work properly (meaning a new record is inserted in the join table user_roles) if the relation is renamed from "roles" to "rolemodel".
So, if there are any avid users of CI's Datamapper that could help, please let me know how to properly define relationships.
UPDATE
You can save an object as a relation using the relationship key:
$object->save( $related, $relationship_key ).
So you would need to use
$user->save($role, "roles");
See the bottom of this web page:
http://datamapper.wanwizard.eu/pages/save.html
Leaving this bit in case it helps someone else out.
It looks like you want to have a custom name on a relationship. (That's what I get after wading through all of the cynicism) -
You get to name the relationship anything that you want with the key in the relationship array. So, in the following snippet, you use book <-- this does or does not have to be the same name as the class - that's what the class key is for.
class Author extends DataMapper {
$has_many = array(
'book' => array( // YOU USE THIS KEY TO NAME THE RELATIONSHIP
'class' => 'book',
'other_field' => 'author',
'join_self_as' => 'author',
'join_other_as' => 'book',
'join_table' => 'authors_books'
)
);
}
If this is not working for you, my guess is you have something else wrong in the set up of your relationships.
http://datamapper.wanwizard.eu/pages/advancedrelations.html
Previously I asked how to ALTER TABLE in Magento setup script without using SQL. There, Ivan gave an excellent answer which I still refer to even now.
However I have yet to discover how to use Varien_Db_Ddl_Table::addColumn() to specify an auto_increment column. I think it has something to do with an option called identity but so far have had no luck.
Is this even possible or is that functionality incomplete?
One can create an autoincrement column like that (at least since Magento 1.6, maybe even earlier):
/** #var $table Varien_Db_Ddl_Table */
$table->addColumn( 'id', Varien_Db_Ddl_Table::TYPE_INTEGER, null, array(
'auto_increment' => true,
'unsigned' => true,
'nullable' => false,
'primary' => true,
), 'ID' );
Instead of "auto_increment", one may also use the keyword "identity".
I think that's something that hasn't been implemented yet.
If you look at the source to addColumn, you can see it looks for a identity/auto_increment option and sets an IDENTITY attribute on the internal column representation.
#File: lib/Varien/Db/Ddl/Table.php
if (!empty($options['identity']) || !empty($options['auto_increment'])) {
$identity = true;
}
$upperName = strtoupper($name);
$this->_columns[$upperName] = array(
'COLUMN_NAME' => $name,
'COLUMN_TYPE' => $type,
'COLUMN_POSITION' => $position,
'DATA_TYPE' => $type,
'DEFAULT' => $default,
'NULLABLE' => $nullable,
'LENGTH' => $length,
'SCALE' => $scale,
'PRECISION' => $precision,
'UNSIGNED' => $unsigned,
'PRIMARY' => $primary,
'PRIMARY_POSITION' => $primaryPosition,
'IDENTITY' => $identity
);
However, if you look at the createTable method on the connection object
#File: lib/Varien/Db/Adapter/Pdo/Mysql.php
public function createTable(Varien_Db_Ddl_Table $table)
{
$sqlFragment = array_merge(
$this->_getColumnsDefinition($table),
$this->_getIndexesDefinition($table),
$this->_getForeignKeysDefinition($table)
);
$tableOptions = $this->_getOptionsDefination($table);
$sql = sprintf("CREATE TABLE %s (\n%s\n) %s",
$this->quoteIdentifier($table->getName()),
implode(",\n", $sqlFragment),
implode(" ", $tableOptions));
return $this->query($sql);
}
you can see _getColumnsDefinition, _getIndexesDefinition, and _getForeignKeysDefinition are used to create a CREATE SQL fragment. None of these methods make any reference to identity or auto_increment, nor do they appear to generate any sql that would create an auto increment.
The only possible candidates in this class are
/**
* Autoincrement for bind value
*
* #var int
*/
protected $_bindIncrement = 0;
which is used to control the increment number for a PDO bound parameter (nothing to do with auto_increment).
There's also a mention of auto_increment here
protected function _getOptionsDefination(Varien_Db_Ddl_Table $table)
{
$definition = array();
$tableProps = array(
'type' => 'ENGINE=%s',
'checksum' => 'CHECKSUM=%d',
'auto_increment' => 'AUTO_INCREMENT=%d',
'avg_row_length' => 'AVG_ROW_LENGTH=%d',
'comment' => 'COMMENT=\'%s\'',
'max_rows' => 'MAX_ROWS=%d',
'min_rows' => 'MIN_ROWS=%d',
'delay_key_write' => 'DELAY_KEY_WRITE=%d',
'row_format' => 'row_format=%s',
'charset' => 'charset=%s',
'collate' => 'COLLATE=%s'
);
foreach ($tableProps as $key => $mask) {
$v = $table->getOption($key);
if (!is_null($v)) {
$definition[] = sprintf($mask, $v);
}
}
return $definition;
}
but this is used to process options set on the table. This auto_increment controls the table AUTO_INCREMENT options, which can be used to control which integer an AUTO_INCREMENT starts at.