I have this table :
+-----+-------------+------------+--------------+---------+--------------+--------------------+-------------------+-------------+----------+------------+--------+---------+
| id | rule_number | profile_id | dest_ip_type | dest_ip | dest_ip_mask | dest_iprange_first | dest_iprange_last | dest_fqdn | protocol | dest_ports | target | enabled |
+-----+-------------+------------+--------------+---------+--------------+--------------------+-------------------+-------------+----------+------------+--------+---------+
| 187 | 3 | 56 | net | 8.8.8.8 | 32 | | | | both | 53 | ACCEPT | 1 |
| 188 | 2 | 56 | range | | 0 | 192.168.1.20 | 192.168.1.250 | | both | 53 | ACCEPT | 1 |
| 189 | 1 | 56 | fqdn | | 0 | | | my.host.com | both | 80 | ACCEPT | 1 |
+-----+-------------+------------+--------------+---------+--------------+--------------------+-------------------+-------------+----------+------------+--------+---------+
From my controller I call Validator depending on "dest_ip_type" :
public function validationNet(Validator $validator)
{
$validator = $this->validationDefault($validator);
$validator
->notEmpty('dest_ip', __('IP is required'))
->notEmpty('dest_ip_mask', __('Mask is required'))
->allowEmpty('dest_iprange_first')
->allowEmpty('dest_iprange_last')
->allowEmpty('dest_fqdn');
$validator
->add('dest_ip', 'notEmpty', [
'rule' => ['ip', 'ipv4'],
'message' => __('Invalid IP Address')
]);
return $validator;
}
public function validationIprange(Validator $validator)
{
$validator = $this->validationDefault($validator);
$validator
->notEmpty('dest_iprange_first', __('IP is required'))
->notEmpty('dest_iprange_last', __('IP is required'))
->allowEmpty('dest_ip')
->allowEmpty('dest_ip_mask')
->allowEmpty('dest_fqdn');
$validator
->add('dest_iprange_first', 'ipv4', [
'rule' => ['ip', 'ipv4'],
'message' => __('Invalid IP Address')
]);
$validator
->add('dest_iprange_last', 'ipv4', [
'rule' => ['ip', 'ipv4'],
'message' => __('Invalid IP Address')
]);
return $validator;
}
public function validationFqdn(Validator $validator)
{
$validator = $this->validationDefault($validator);
$validator
->notEmpty('dest_fqdn')
->allowEmpty('dest_iprange_first')
->allowEmpty('dest_iprange_last')
->allowEmpty('dest_ip')
->allowEmpty('dest_ip_mask');
$validator
->add('dest_fqdn', 'notEmpty', [
// Regex for FQDN
'rule' => ['custom', "/(?=^.{4,253}$)(^((?!-)[a-zA-Z0-9-]{1,63}(?<!-)\.)+[a-zA-Z]{2,63}$)/i"],
'message' => __('Invalid Hostname')
]);
return $validator;
}
I created 3 RulesChecker bellow to check unique differently, but How can I tell Validator or Controller to not call default "buildRules" function but force to call alternative buildRules ?
public function buildNetRules(RulesChecker $rules)
{
$rules->add($rules->existsIn(['profile_id'], 'Profiles'));
$rules->add($rules->isUnique(['profile_id', 'dest_ip', 'protocol', 'dest_ports']));
return $rules;
}
public function buildRangeRules(RulesChecker $rules)
{
$rules->add($rules->existsIn(['profile_id'], 'Profiles'));
$rules->add($rules->isUnique(['profile_id', 'dest_iprange_first', 'dest_iprange_last', 'protocol', 'dest_ports']));
return $rules;
}
public function buildFqdnRules(RulesChecker $rules)
{
$rules->add($rules->existsIn(['profile_id'], 'Profiles'));
$rules->add($rules->isUnique(['profile_id', 'dest_fqdn', 'protocol', 'dest_ports']));
return $rules;
}
I found the solution by my self. buildRules is able to check different isunique rules inside de same function. Maybe buildRules don't check isUnique rules on values that are Empty or null...
public function buildRules(RulesChecker $rules)
{
$rules->add($rules->existsIn(['profile_id'], 'Profiles'));
$rules->add($rules->isUnique(['profile_id', 'dest_ip', 'protocol', 'dest_ports']));
$rules->add($rules->isUnique(['profile_id', 'dest_iprange_first', 'dest_iprange_last', 'protocol', 'dest_ports']));
$rules->add($rules->isUnique(['profile_id', 'dest_fqdn', 'protocol', 'dest_ports']));
return $rules;
}
Related
I am trying to show display a "join" result and only want to show the user_id, username, email only once for users table even if there are many records on the other table, so I tried to make a query builder like below:
Table users | Table add_game
|
user_id | username | email | game_id | user_id | ign | acc_id
1 | userA | userA#email.com | 1 | 1 | ignA | accA
2 | userB | userB#gmail.com | 2 | 1 | ignB | accB
| 1 | 2 | ignB | accB
| 3 | 2 | ignD | accD
Model :
<?php namespace App\Models;
use CodeIgniter\Database\ConnectionInterface;
class LoginModel{
protected $db;
public function __construct(ConnectionInterface &$db){
$this->db =& $db;
}
public function login(string $str)
{
return $this->db->table('users')
->groupStart()
->where('username', $str)
->orWhere('email', $str)
->groupEnd()
->join('add_game', 'add_game.user_id = users.user_id')
//->distinct('users.user_id')
//->select(("GROUP_CONCAT(game_id, ign, acc_id) AS userdata"))
->get()
->getResultArray();
}
}
Controller :
public function login()
{
$data = [];
helper(['form']);
$validation = \Config\Services::validation();
$db = db_connect();
$model = new LoginModel($db);
$user = $model->login($this->request->getVar('userlogin'));
$this->setUserSession($user);
echo view('templates/header', $data);
echo view('account/login', $data);
echo view('templates/footer', $data);
}
private function setUserSession($user){
$data = [
'user_id' => $user['user_id'],
'username' => $user['username'],
'email' => $user['email'],
'firstname' => $user['firstname'],
'lastname' => $user['lastname'],
'dob' => $user['dob'],
'country' => $user['country'],
'country_code' => $user['c_code'],
'contact' => $user['contact'],
'game_id' => $user['game_id'],
'ign' => $user['ign'],
'acc_id' => $user['acc_id'],
'isLoggedIn' => true
];
session()->set($data);
return true;
}
But right now I am getting
Undefined index: user_id
error message. Previously there was no issue or error when I was using without query builder for my login :
public function login(string $str, string $fields, array $data)
{
return $this->where('username', $data['userlogin'])->orWhere('email', $data['userlogin'])
->first();
}
How to resolve this error?
As by your comment (image) your array looks like:
Array
(
[0]=>Array
(
[user_id]=>1,
[user_name]=>'test',
//etc.
)
)
You get the
Undefined index: user_id
error message, because of addressing wrongly the array while using 'user_id' => $user['user_id']
the correct way is to add the index you want to retrieve like:
$this->setUserSession($user[0]); // where 0 can be changed to the index you pretend
now the array is flattened and 'user_id' => $user['user_id'] doesn't throw an error anymore.
As per the title, I have a single excel file full of data (parent/child) in each row. The Laravel Excel site shows how to import, assuming a single model per row - but how does one import the child data?
https://laravel-excel.maatwebsite.nl/3.1/imports/
eg 'pseudo' schema:
Schema::create('students', function (Blueprint $table) {
$table->increments('id')->unsigned()->index();
$table->string('student_code',16);
$table->string('student_name',64);
$table->string('student_surname',64);
});
Schema::create('student_courses', function (Blueprint $table) {
$table->increments('id')->unsigned()->index();
$table->integer('student_id')->unsigned()->index();
$table->string('course', 32);
$table->date('start_date');
$table->date('end_date');
$table->timestamps();
});
Schema::create('student_contacts', function (Blueprint $table) {
$table->increments('id')->unsigned()->index();
$table->integer('student_id')->unsigned()->index();
$table->string('contact_name', 32);
$table->string('contact_number', 32);
$table->timestamps();
});
Eg File: students.xlsx
Student Code | Student Name | Student Surname | Course | Course Start Date | Course End Date | Contact Person | Contact Numbers
ABC1 | Arnold | Clarp | C++ | 2019-01-01 | 2019-12-01 | Boogle | 555-111-222
DEF2 | Delta | Flork | English | 2019-01-02 | 2019-12-02 | Google | 555-111-333
GHI3 | Goblin | Clark | Science | 2019-01-03 | 2019-12-03 | Foogle | 555-111-444
Assuming my import code:
class StudentsImport implements ToModel, WithStartRow
{
/**
* #param array $row
*
* #return \Illuminate\Database\Eloquent\Model|null
*/
public function model(array $row)
{
return new Student([
'student_code' => $row[1],
'student_name' => $row[2],
'student_surname' => $row[3],
]);
}
/**
* #return int
*/
public function startRow(): int
{
return 2;
}
}
Where would I actually go about plugging in the import of the "child" course/contact data?
I'm guessing that instead of 'returning a new student' - I would actually first assign it to a variable, then import it then? Is this the correct way?
eg:
public function model(array $row)
{
$student = new Student([
'student_code'=> $row[1],
'student_name'=> $row[2],
'student_surname'=> $row[3],
])
$student->courses()->create([
'course'=>$row[4],
'start_date'=>$row[5],
'end_date'=>$row[6]
]);
$student->contacts()->create([
'contact_name'=>$row[7],
'contact_number'=>$row[8]
]);
return $student;
}
//actual code as it stands, no longer 'pseudo':
$student = new Student([
'bursary_provider_id' => 1,
'bursary_provider_reference' => 'xxx',
'student_name' => $row[1],
'student_initials' => $row[3],
'student_surname' => $row[2],
'passport_number' => $row[7],
'passport_expiration' => \PhpOffice\PhpSpreadsheet\Shared\Date::excelToDateTimeObject($row[9]),
'country_id' => 7,
'id_number' => $row[6],
'status' => $status,
'notes' => $row[5]
]);
if (isset($row[10])) {
$student->visas()->create([
'country_id' => 201,
'visa_reference_number' => $row[10],
'visa_expiration_date' => \PhpOffice\PhpSpreadsheet\Shared\Date::excelToDateTimeObject($row[11])
]);
Error (its not passing across the parent id)
SQLSTATE[23000]: Integrity constraint violation: 1048 Column 'student_id' cannot be null (SQL: insert into student_visas (country_id, visa_reference_number, visa_expiration_date, student_id, updated_at, created_at) values (201, ABCHFV4, 2019-12-31 00:00:00, , 2019-01-11 08:03:06, 2019-01-11 08:03:06))
You get the error because the student is not yet written to de database, so no id has been assigned to the new student instance. If you use collections the data is immediately written to the database. In the model function you can use
$student=Student::Create(['bursary_provider_id' => 1,
'bursary_provider_reference' => 'xxx',
'student_name' => $row[1],
'student_initials' => $row[3],
'student_surname' => $row[2],
'passport_number' => $row[7],
'passport_expiration' => \PhpOffice\PhpSpreadsheet\Shared\Date::excelToDateTimeObject($row[9]),
'country_id' => 7,
'id_number' => $row[6],
'status' => $status,
'notes' => $row[5]]);
This will write the student to the database and generate the neccessary id. I am not sure how this affects performance though.
The answer, (and do not ask me why, because documentation is so poor at this stage) is to use collections.
use Illuminate\Support\Collection;
use Maatwebsite\Excel\Concerns\ToCollection;
and instead of ToModel:
public function collection(Collection $rows)
and it doesnt 'return' anything. Other than these changes, using the exact same code in OP works as intended.
I am using CakePHP3, I created de Config model with this table :
+----------------------+---------------------------+
| param | value |
+----------------------+---------------------------+
| dhcp_dns1 | 192.168.1.100 |
| dhcp_dns2 | 192.168.1.100 |
| dhcp_enabled | 1 |
| dhcp_end_ip | 192.168.1.200 |
| dhcp_netmask | 255.255.255.0 |
| dhcp_router | 192.168.1.100 |
| dhcp_start_ip | 192.168.1.100 |
| host_name | box |
I am using a pairs of param/value. param will not be alterable but only value will be alterable.
I have different kind of value (ip address, text, numeric) that I need to validate before change.
So the controller use multi-step save data, to validate different kind of values.
I created a Form (for test) to update host_name (alphaNumeric Validation) and dhcp_start_ip (ipv4 Validation).
For validation I used this :
http://book.cakephp.org/3.0/en/orm/validation.html#using-a-different-validation-set
Validation works fine but it not display message if validation not match. How I can show message to say Hostname or IP address is wrong format when patchEntity ?
Maybe I am in the wrong way to do it...
This is the view :
<div class="config form large-10 medium-9 columns">
<fieldset>
<legend><?= __('DHCP Config')?></legend>
<?= $this->Form->create('config') ?>
<?php
//$params = array('host_name', 'host_ip', 'host_netmask', 'host_gateway', 'host_dns1', 'host_dns2');
echo $this->Form->input('host_name',[
'label' => __('Hostname : '),
'default' => $host_name->value
]);
echo $this->Form->input('dhcp_start_ip',[
'label' => __('DHCP Pool start : '),
'default' => $dhcp_start_ip->value
]);
?>
</fieldset>
<?= $this->Form->button(__('Submit')) ?>
<?= $this->Form->end() ?>
</div>
Controller :
public function test()
{
$host_name = $this->Config->get('host_name', ['contain' => []]);
$dhcp_start_ip = $this->Config->get('dhcp_start_ip', ['contain' => []]);
if ($this->request->is(['patch', 'post', 'put'])) {
$confdata = array(
'param' => 'dhcp_start_ip',
'value' => $this->request->data['dhcp_start_ip']
);
$host_name_data = array(
'param' => 'host_name',
'value' => $this->request->data['host_name']
);
// 1ere methode de validation
$dataconfig = $this->Config->patchEntity($dhcp_start_ip, $confdata, [
'validate' => 'ipaddr',
]);
$this->Config->save($dataconfig);
$dataconfig = $this->Config->patchEntity($host_name, $host_name_data, [
'validate' => 'name',
]);
$this->Config->save($dataconfig);
}
$this->set('dhcp_start_ip', $dhcp_start_ip);
$this->set('host_name', $host_name);
}
Model :
public function validationIpaddr(Validator $validator)
{
$validator->notEmpty('value')
->add('value', 'notEmpty', [
'rule' => ['ip', 'ipv4'],
'message' => __('Invalid IP Address')
]);
return $validator;
}
public function validationName(Validator $validator)
{
$validator
->add('value', 'notEmpty', [
'rule' => ['alphaNumeric'],
'message' => __('Invalid Name')
]);
return $validator;
}
Try this, In your controller
$dataconfig = $this->Config->patchEntity($dhcp_start_ip, $confdata, [
'validate' => 'ipaddr',
]);
if (!$dataconfig->errors()) {
$this->Config->save($dataconfig);
}else{
debug($dataconfig->errors());
$this->Flash->error('Invalid IP Address!');
}
I am creating simple messenger for Laravel 4, with fuctionality pretty same as Facebook messenger. I have problem making Eloquent query to list latest messages with threads for currently logged user.
Consider I have the following DB tables for messenger (there is also users table):
+-----------+ +--------------+ +----------------+ +----------------+ +-----+
|threads | |thread_users | |messages | |messages_status | |users|
+-----------+ +--------------+ +----------------+ +----------------+ +-----+
|id | |id | |id | |id | |id |
|title | |thread_id | |thread_id | |user_id | |name |
|created_at | |user_id | |sender_id (user)| |message_id | +-----+
|deleted_at | |created_at | |content | |deleted_at |
+-----------+ |deleted_at | |created_at | +----------------+
+--------------+ +----------------+
DB explanation:
When user writes first message he creates thread. All users added to thread are listed in thread_users table. Message submitted to thread are stored in messages table, which is linked to messages_status table where i'm storing info if user deleted particular message from thread for his account.
Laravel models (i have removed SoftDeletingTrait to make it clear):
class threads extends \Eloquent {
protected $table = 'threads';
public function participants() {
return $this->hasMany('messengerThreadUsers', 'thread_id', 'id');
}
public function userParticipation() {
return $this->hasOne('threadUsers', 'thread_id', 'id')->where('user_id', '=', Auth::user()->id);
}
public function messages() {
return $this->hasMany('message', 'thread_id', 'id');
}
public function lastMessage() {
return $this->hasOne('message', 'thread_id', 'id')->latest();
}
}
class threadUsers extends \Eloquent {
protected $table = 'thread_users';
}
class message extends \Eloquent {
protected $table = 'messages';
public function statuses() {
return $this->hasMany('messageStatus', 'message_id', 'id');
}
public function thread() {
return $this->hasOne('threads', 'id', 'thread_id');
}
}
class messageStatus extends \Eloquent {
protected $table = 'messages_status';
public function message() {
return $this->hasOne('message', 'id', 'message_id');
}
public function thread() {
return $this->hasOne('threads', 'id', 'thread_id');
}
}
Now i want to make query that return 5 most recent messages (ordered by message creation date) together with threads, for currently logged user, but where message_status.deleted_at is NULL.
So far i've got this:
$this->threads->select(\DB::raw('threads.*'))
->with('lastMessage')
->has('userParticipation')
->join('messages', 'messages.thread_id', '=', 'threads.id')
->orderBy('messages.created_at', 'DESC')
->paginate(5);
But it keeps returning me dupicate threads. In DB i have 2 threads:
thread #1 have 2 messages in it
thread #2 have 1 message in it
output below:
0 =>
array (size=5)
'id' => int 10
'title' => string 'THREAD 1' (length=8)
'deleted_at' => null
'created_at' => string '2015-07-06 14:20:03' (length=19)
'last_message' =>
array (size=7)
'id' => int 11
'sender_id' => int 12
'thread_id' => int 10
'content' => string 'MSG 1 in thread 1' (length=129)
'created_at' => string '2015-07-07 19:36:52' (length=19)
1 =>
array (size=5)
'id' => int 10
'title' => string 'THREAD 1' (length=8)
'deleted_at' => null
'created_at' => string '2015-07-06 14:20:03' (length=19)
'last_message' =>
array (size=7)
'id' => int 11
'sender_id' => int 12
'thread_id' => int 10
'content' => string 'MSG 1 in thread 1' (length=129)
'created_at' => string '2015-07-07 19:36:52' (length=19)
2 =>
array (size=5)
'id' => int 10
'title' => string 'THREAD 2' (length=8)
'deleted_at' => null
'created_at' => string '2015-07-06 14:25:13' (length=19)
'last_message' =>
array (size=7)
'id' => int 11
'sender_id' => int 12
'thread_id' => int 10
'content' => string 'MSG 1 in thread 2' (length=129)
'created_at' => string '2015-07-07 19:56:22' (length=19)
Extending core functionality, adding new column. Grid gets built, query builds w/ data but the when the row is generated it fails to add populate the 'visibility' cell in the row. Thoughts?
EDIT: For clarity.
output of zend_debug($row)
class Mage_Catalog_Model_Product#751 (28) {
protected $_cacheTag => string(15) "catalog_product"
protected $_eventPrefix => string(15) "catalog_product"
protected $_eventObject => string(7) "product"
protected $_canAffectOptions => bool(false)
protected $_typeInstance => NULL
protected $_typeInstanceSingleton => NULL
protected $_linkInstance => NULL
protected $_customOptions => array(0) {
}
protected $_urlModel => NULL
protected $_errors => array(0) {
}
protected $_optionInstance => NULL
protected $_options => array(0) {
}
protected $_reservedAttributes => NULL
protected $_isDuplicable => bool(true)
protected $_defaultValues => array(0) {
}
protected $_lockedAttributes => array(0) {
}
protected $_isDeleteable => bool(true)
protected $_isReadonly => bool(false)
protected $_resourceName => string(15) "catalog/product"
protected $_resource => NULL
protected $_resourceCollectionName => string(26) "catalog/product_collection"
protected $_dataSaveAllowed => bool(true)
protected $_isObjectNew => NULL
protected $_data => array(13) {
'entity_id' => string(4) "2384"
'entity_type_id' => string(1) "4"
'attribute_set_id' => string(1) "4"
'type_id' => string(6) "simple"
'sku' => string(0) ""
'has_options' => string(1) "0"
'required_options' => string(1) "0"
'created_at' => string(19) "2012-06-22 16:19:32"
'updated_at' => string(19) "2012-07-03 14:06:08"
'position' => NULL
'name' => string(25) "Games Site Test Product 4"
'price' => string(8) "100.0000"
'stock_item' => class Varien_Object#792 (5) {
protected $_data => array(1) {
...
}
protected $_hasDataChanges => bool(false)
protected $_origData => NULL
protected $_idFieldName => NULL
protected $_isDeleted => bool(false)
}
}
protected $_hasDataChanges => bool(true)
protected $_origData => array(12) {
'entity_id' => string(4) "2384"
'entity_type_id' => string(1) "4"
'attribute_set_id' => string(1) "4"
'type_id' => string(6) "simple"
'sku' => string(0) ""
'has_options' => string(1) "0"
'required_options' => string(1) "0"
'created_at' => string(19) "2012-06-22 16:19:32"
'updated_at' => string(19) "2012-07-03 14:06:08"
'position' => NULL
'name' => string(25) "Games Site Test Product 4"
'price' => string(8) "100.0000"
}
protected $_idFieldName => string(9) "entity_id"
protected $_isDeleted => bool(false)
}
,
sqdump (i added LIMIT 10)
mysql> SELECT `e`.*, `_table_position`.`position`, `_table_visibility`.`value` AS `visibility` FROM `catalog_product_entity` AS `e` LEFT JOIN `catalog_category_product` AS `_table_position` ON (_table_position.product_id=e.entity_id) AND (category_id=0) INNER JOIN `catalog_product_entity_int` AS `_table_visibility` ON (_table_visibility.entity_id = e.entity_id) AND (_table_visibility.attribute_id='89') AND (_table_visibility.store_id=0) limit 10;
+-----------+----------------+------------------+---------+---------+-------------+------------------+---------------------+---------------------+----------+------------+
| entity_id | entity_type_id | attribute_set_id | type_id | sku | has_options | required_options | created_at | updated_at | position | visibility |
+-----------+----------------+------------------+---------+---------+-------------+------------------+---------------------+---------------------+----------+------------+
| 2 | 4 | 9 | simple | PWOPER | 0 | 0 | 2010-06-09 20:14:44 | 2012-01-09 19:20:06 | NULL | 1 |
| 3 | 4 | 9 | simple | PW20WCB | 0 | 0 | 2010-06-10 14:36:04 | 2012-07-03 16:46:09 | NULL | 4 |
| 20 | 4 | 9 | simple | PW5TBW | 0 | 0 | 2010-06-28 15:44:23 | 2011-04-08 21:35:02 | NULL | 1 |
| 21 | 4 | 9 | simple | PWBJACK | 0 | 0 | 2010-06-28 15:51:35 | 2011-10-27 15:26:21 | NULL | 1 |
| 22 | 4 | 9 | simple | YK32006 | 0 | 0 | 2010-06-28 16:22:25 | 2012-07-03 16:47:46 | NULL | 4 |
| 23 | 4 | 9 | simple | YK32008 | 0 | 0 | 2010-06-28 16:30:51 | 2012-07-03 16:48:55 | NULL | 4 |
| 24 | 4 | 9 | simple | RFBURG | 0 | 0 | 2010-06-28 16:37:12 | 2011-09-16 14:03:04 | NULL | 4 |
| 25 | 4 | 9 | simple | PW10EWJ | 0 | 0 | 2010-06-28 17:34:15 | 2011-08-11 15:43:49 | NULL | 1 |
| 26 | 4 | 9 | simple | PW10WJ | 0 | 0 | 2010-06-28 17:39:26 | 2011-08-11 16:04:37 | NULL | 4 |
| 27 | 4 | 9 | simple | HDNG10 | 0 | 0 | 2010-06-28 17:48:00 | 2012-07-03 17:00:07 | NULL | 4 |
+-----------+----------------+------------------+---------+---------+-------------+------------------+---------------------+---------------------+----------+------------+
10 rows in set (0.00 sec)
mysql>
_prepareCollection()
class My_CategoryFilters_Block_Adminhtml_Catalog_Category_Tab_Product extends Mage_Adminhtml_Block_Catalog_Category_Tab_Product {
...
protected function _prepareCollection()
{
if ($this->getCategory()->getId()) {
$this->setDefaultFilter(array('in_category'=>1));
}
#$store = $this->_getStore();
$collection = Mage::getModel('catalog/product')->getCollection()
->addAttributeToSelect('name')
->addAttributeToSelect('sku')
->addAttributeToSelect('price')
->addAttributeToSelect('store_id')
#->addAttributeToSelect('visibility')
->addStoreFilter($this->getRequest()->getParam('store'))
->joinField('position',
'catalog/category_product',
'position',
'product_id=entity_id',
'category_id='.(int) $this->getRequest()->getParam('id', 0),
'left')
->joinAttribute('visibility', 'catalog_product/visibility', 'entity_id', 'entity_id', 'entity_id='.(int) $this->getRequest()->getParam('id', 0), 'inner')
;
$this->setCollection($collection);
$this->getCollection()->addWebsiteNamesToResult();
if ($this->getCategory()->getProductsReadonly()) {
$productIds = $this->_getSelectedProducts();
if (empty($productIds)) {
$productIds = 0;
}
$this->getCollection()->addFieldToFilter('entity_id', array('in'=>$productIds));
}
return parent::_prepareCollection();
}
_prepareColumns()
$this->addColumn('visibility', array(
'header' => Mage::helper('catalog')->__('Visibility'),
'width' => '100',
'sortable' => false,
'index' => 'visibility',
'type' => 'options',
'options' => Mage::getModel('catalog/product_visibility')->getOptionArray(),
));
Looking at the product collection catalog/model/resource/product/collection.php a few things I see that may be the culprit:
/**
* Apply limitation filters to collection
* Method allows using one time category product index table (or product website table)
* for different combinations of store_id/category_id/visibility filter states
* Method supports multiple changes in one collection object for this parameters
*
* #return Mage_Catalog_Model_Resource_Product_Collection
*/
protected function _applyProductLimitations()
{
$this->_prepareProductLimitationFilters();
It appears Magento is blocking and/or routing the visibility field from the collection.
Further looking into _prepareProductLimitationFilters() method itself reveals:
/**
* Apply front-end price limitation filters to the collection
*
* #return Mage_Catalog_Model_Resource_Product_Collection
*/
public function applyFrontendPriceLimitations()
{
$this->_productLimitationFilters['use_price_index'] = true;
if (!isset($this->_productLimitationFilters['customer_group_id'])) {
$customerGroupId = Mage::getSingleton('customer/session')->getCustomerGroupId();
$this->_productLimitationFilters['customer_group_id'] = $customerGroupId;
}
if (!isset($this->_productLimitationFilters['website_id'])) {
$websiteId = Mage::app()->getStore($this->getStoreId())->getWebsiteId();
$this->_productLimitationFilters['website_id'] = $websiteId;
}
$this->_applyProductLimitations();
return $this;
}
How to get around this without causing havoc on the "reasoning" as to why this I'm not sure of.
Figured it out.
My implementation extends Mage_Adminhtml_Block_Catalog_Category_Tab_Product. Because it extends Mage_Adminhtml_Block_Catalog_Category_Tab_Product, I am very restricted as to what I can do w/ the extended methods. The reason the new column's data does not appear is because the parent method has no access to the data (->addAttribute('visibility')).
In order to handle the new data, I need to extend Mage_Adminhtml_Block_Widget_Grid instead. The widget provides more generalized methods for handling data.