I'm new in Laravel 9 but for my project I need to create a pivot table with multiple foreign keys. This is what the migration looks like.
public function up()
{
Schema::create('language_user', function (Blueprint $table) {
$table->id();
$table->foreignId('language_id')->constrained()->onDelete('cascade');
$table->foreignId('user_id')->constrained()->onDelete('cascade');
$table->foreignId('proficiency_id')->constrained()->default(1)->onDelete('cascade');
$table->foreignId('motivation_id')->constrained()->default(1)->onDelete('cascade');
$table->foreignId('lang_status_id')->constrained()->default(1)->onDelete('cascade');
$table->timestamps();
});
Now, i'm trying to insert the data from a Livewire component, so I tried to create a Model for the pivot table but it didn't work. Can you point me in the right direction?
I removed the default values from the migration, from some reason they weren't working. The I used this in the Livewire controller:
public function addLanguage(Language $language)
{
$user = auth()->user();
$data = $this->validate();
$language->id = $data['language'];
//create user language record
$language->users()->attach($user->id, ['proficiency_id' => 1 , 'motivation_id' => 1, 'lang_status_id' => 1]);
//message
//redirect
return redirect(route('dashboard'));
}
And this worked. I hope this can help someone else.
I removed the default values from the migration, from some reason they weren't working. The I used this in the Livewire controller:
public function addLanguage(Language $language)
{
$user = auth()->user();
$data = $this->validate();
$language->id = $data['language'];
//create user language record
$language->users()->attach($user->id, ['proficiency_id' => 1 , 'motivation_id' => 1, 'lang_status_id' => 1]);
//message
//redirect
return redirect(route('dashboard'));
}
And this worked. I hope this can help someone else.
Related
By overriding the delete function of GroceryCRUD as below, we can easily soft-delete a record.
public function user() {
$crud = new grocery_CRUD();
$crud->set_table('cms_user');
$crud->set_subject('User List');
$crud->required_fields('user_name');
$crud->columns('user_name','email','real_name','active');
$crud->change_field_type('active', 'true_false');
$crud->callback_delete(array($this,'delete_user'));
$output = $crud->render();
$this->_example_output($output);
}
public function delete_user($primary_key) {
return $this->db->update(
'cms_user',
array('deleted' => '1'),
array('id' => $primary_key)
);
}
My question is, How do I hide the soft-deleted records in the Main View ??
Thank you.
The answer was easy... after some googling it is obvious I can use the where clause
$crud->where('deleted', '0');
Thank you all... works like a charm
I have a many to many relation between Product and Properties. I'm using embedRelation() in my Product form to edit a Product and it's Properties. Properties includes images which causes my issue. Every time I save the form the updated_at column is updated for file properties even when no file is uploaded.
Therefore, I want to exclude empty properties when saving my form.
I'm using Symfony 1.4 and Doctrine 1.2.
I'm thinking something like this in my ProductForm.class.php, but I need some input on how to make this work.
Thanks
class ProductForm extends BaseProductForm
{
public function configure()
{
unset($this['created_at'], $this['updated_at'], $this['id'], $this['slug']);
$this->embedRelation('ProductProperties');
}
public function saveEmbeddedForms($con = null, $forms = null)
{
if (null === $forms)
{
$properties = $this->getValue('ProductProperties');
$forms = $this->embeddedForms;
foreach($properties as $p)
{
// If property value is empty, unset from $forms['ProductProperties']
}
}
}
}
I ended up avoiding Symfony's forms and saving models instead of saving forms. It can be easier when playing with embedded forms. http://arialdomartini.wordpress.com/2011/04/01/how-to-kill-symfony%E2%80%99s-forms-and-live-well/
Solved it by checking if posted value is a file, and if both filename and value_delete is null I unset from the array. It might not be best practice, but it works for now.
Solution based on http://www.symfony-project.org/more-with-symfony/1_4/en/06-Advanced-Forms
class ProductPropertyValidatorSchema extends sfValidatorSchema
{
protected function configure($options = array(), $messages = array())
{
// N0thing to configure
}
protected function doClean($values)
{
$errorSchema = new sfValidatorErrorSchema($this);
foreach($values as $key => $value)
{
$errorSchemaLocal = new sfValidatorErrorSchema($this);
if(array_key_exists('value_delete', $values))
{
if(!$value && !$values['value_delete'])
{
unset($values[$key]);
}
}
// Some error for this embedded-form
if (count($errorSchemaLocal))
{
$errorSchema->addError($errorSchemaLocal, (string) $key);
}
}
// Throws the error for the main form
if (count($errorSchema))
{
throw new sfValidatorErrorSchema($this, $errorSchema);
}
return $values;
}
}
I have a number of FilteringSelect elements within my Zend Framework application that are working fine but they are based on simple queries.
I now need to create a FilteringSelect that will allow me to select the id of one table while displaying the text of field in a related table, i.e. I have two tables groomservices and groomprocedures which are related (i.e. groomprocedures.groomProceduresID has many groomservices.procedure).
The form I'm trying to create is for an appointments table which has many groomservices.groomServicesID values. I want the user to be able to see the name of the procedure while saving the value of the groomservices.groomServicesID using the FilteringSelect.
So far I've not been able to do this in that my FilteringSelect displays nothing, I'm sure this can be done just that the fault is with my inexperience with Zend,Doctrine and Dojo
I'm not sure if my problem is with my autocomplete action(including the query) or with the FilteringSelect element.
Can anyone spot where I've gone wrong in the code sections below, I need to get this working.
My autocomplete action within my controller
public function gserviceAction()
{
// disable layout and view rendering
$this->_helper->layout->disableLayout();
$this->getHelper('viewRenderer')->setNoRender(true);
// get a list of all grooming services IDs and related procedures
$qry= Doctrine_Query::create()
->select('g.groomServicesID,p.groomProcedure')
->from('PetManager_Model_Groomservices g')
->leftJoin('g.PetManager_Model_Groomprocedures p');
$result=$qry->fetchArray();
//generate and return JSON string
$data = new Zend_Dojo_Data('g.groomServicesID',$result);
echo $data->toJson();
}
My FilteringSelect element code
// Create a autocomplete select input for the service
$gservice = new Zend_Dojo_Form_Element_FilteringSelect('gapmtService');
$gservice->setLabel('Proceedure');
$gservice->setOptions(array(
'autocomplete' => true,
'storeID' => 'gserviceStore',
'storeType' => 'dojo.data.ItemFileReadStore',
'storeParams' => array('url' => "/groomappointments/appointment/gservice"),
'dijitParams' => array('searchAttr' => 'groomProcedure')))
->setRequired(true)
->addValidator('NotEmpty', true)
->addFilter('HTMLEntities')
->addFilter('StringToLower')
->addFilter('StringTrim');
Many thanks in advance,
Graham
P.S. orgot to mention I tried the following query in mysql and I gave me what I'm looking for I believe the Doctine query evaluates to the same.
select groomservices.groomservicesID,groomprocedures.groomprocedure from groomprocedures left join groomservices on groomprocedures.groomproceduresID =groomservices.groomProcedure
But I'm not sure if I formatted the query correctly in Doctrine.
EDIT in relation to the flammon's comments
Ok I've set the code to the following but I'm still not getting anything to display.
public function gserviceAction()
{
$ajaxContext = $this->_helper->getHelper('AjaxContext');
$ajaxContext->addActionContexts(array(
'gservice' => 'json'
));
// get a list of all grooming services IDs and related procedures
$qry= Doctrine_Query::create()
->select('g.groomServicesID AS id,p.groomprocedure AS name')
->from('PetManager_Model_Groomservices g')
->leftJoin('g.PetManager_Model_Groomprocedures p');
$this->view->model = (object) array();
$this->view->model->identifier = 'id';
$this->view->model->label = 'name';
$this->view->model->items = array();
$tableRows = $this->dbTable->fetchAll($qry);
foreach ($tableRows as $row) {
$this->view->model->items[] = $row->toArray();
}
}
I'm sure the fault lies with me.
It looks like there's a problem with the data that you're putting in the ItemFileReadStore.
Here are a few pointers.
Consider extending Zend_Rest_Controller for your services. It'll be easier to manage your contexts and your views. You'll be able to do something like this:
public function init()
{
$ajaxContext = $this->_helper->getHelper('AjaxContext');
$ajaxContext->addActionContexts(array(
'gservice' => 'json'
));
}
And it will eliminate the need for the following in each of you service actions.
// disable layout and view rendering
$this->_helper->layout->disableLayout();
$this->getHelper('viewRenderer')->setNoRender(true);
You'll need to either pass the format parameter or use the following plugin to help with the context switch. Passing the format parameter is simpler but it pollutes the url with ?format=json. Here's the Zend documentation on AjaxContext.
Here's a plugin that you can use if you don't want to pass the format parameter.
class Application_Plugin_AcceptHandler extends Zend_Controller_Plugin_Abstract
{
public function dispatchLoopStartup(Zend_Controller_Request_Abstract $request)
{
if (!$request instanceof Zend_Controller_Request_Http) {
return;
}
$header = $request->getHeader('Accept');
switch (true) {
case (strstr($header, 'application/json')):
Zend_Registry::get('logger')->log('Setting format to json', Zend_Log::INFO);
$request->setParam('format', 'json');
break;
case (strstr($header, 'application/xml')
&& (!strstr($header, 'html'))):
Zend_Registry::get('logger')->log('Setting format to xml', Zend_Log::INFO);
$request->setParam('format', 'xml');
break;
default:
Zend_Registry::get('logger')->log('Setting format to html', Zend_Log::INFO);
break;
}
}
}
In your controller, instead of echoing the data, create view variables that dojo expects. See this document for the format.
$this->view->model = (object) array();
$this->view->model->identifier = 'id';
$this->view->model->label = 'name';
$this->view->model->items = array();
In your controller, fetch your table rows:
$tableRows = $this->dbTable->fetchAll($select);
or, if you've put model code in a function, it might look more like:
$tableRows = $this->dbTable->fetchGroomProcedures();
Put your row data in the model->items[] array:
foreach ($tableRows as $row) {
$this->view->model->items[] = $row->toArray();
}
Create a view, view/scripts/appointment/gservice.json.phtml and in it put
Zend_Json::encode($this->model)
Use Firebug to see what is returned from your service.
I get a really anoying error when I try to edit an entry from a table, in tutorial they always use getTable()->find(), but I need to verify that the person logged in is the owner of that entry here what I did:
In the action:
public function executeEdit(sfWebRequest $request)
{
$id = $request->getParameter('id');
$userid = $this->getUser()->getGuardUser()->getId();
$ad = Doctrine_Core::getTable('BambinbazarArticles')->getMyAd($id, $userid);
$this->forward404Unless($ad, sprintf('Object bambinbazar_articles does not exist (%s).', $request->getParameter('id')));
$this->form = new BambinbazarArticlesForm($ad);
}
In the model:
public function getMyAd($id, $userid)
{
$q = $this->createQuery('c')
->where('c.id = ? ', $id)
->addWhere('c.userid = ? ', $userid);
return $q->execute();
}
I tried it with and without the ->execute(), did doctrine clean, cleared cache, rebuilded model,
Always get the same error 'The "%s" form only accepts a "%s" object.
If I use the Doctrine_Core::getTable('BambinbazarArticles')->find() it work, but of course, i need more than that..
I am becoming crazy over this.
execute() can return multiple rows; effectively you're getting a recordset back, rather than the individual object that your form is expecting. Try fetching a single object, using, e.g.:
return $q->execute()->getFirst();
or
return $q->fetchOne();
Its probably because your query is returning a Doctrine_Collection, not the actual Doctrine_Record youre expecting. Instead of execute use fetchOne.
public function getMyAd($id, $userid)
{
$q = $this->createQuery('c')
->where('c.id = ? ', $id)
->addWhere('c.userid = ? ', $userid)
->limit(1);
return $q->fetchOne();
}
Currently, I'm getting a regular DbTable Auth Adapter:
protected function _getAuthAdapter($formData)
{
$dbAdapter = Zend_Db_Table::getDefaultAdapter();
$authAdapter = new Zend_Auth_Adapter_DbTable($dbAdapter);
$authAdapter->setTableName('users')
->setIdentityColumn('username')
->setCredentialColumn('password');
$authAdapter->setIdentity($formData['username']);
$authAdapter->setCredential(md5($formData['password']));
return $authAdapter;
}
But I want to check an additional column in the database (IsActive for example). I don't know if this can be done with the adapter. How can this be done?
I have a similar situation, and I extended the Zend_Auth_Adapter_DbTable to satisfy my needs:
class My_Auth_Adapter_DbTable extends Zend_Auth_Adapter_DbTable
{
protected $_customerIdColumn = 'customer_id';
protected $_customerId = false;
public function setCustomerId($id) {
$this->_customerId = $id;
return $this;
}
public function getCustomerId() {
return ($this->_customerId !== false) ? $this->_customerId : '';
}
public function _authenticateCreateSelect() {
$dbSelect = parent::_authenticateCreateSelect();
$dbSelect->where($this->_zendDb->quoteIdentifier($this->_customerIdColumn, true) . ' = ?',
$this->getCustomerId());
return $dbSelect;
}
}
I then use it like this:
public function getAuthAdapter(array $params)
{
$authAdapter = new My_Auth_Adapter_DbTable(
$params['db_adapter'],
'users',
'username',
'password',
'? AND active = 1'
);
$authAdapter->setIdentity($params['username'])
->setCustomerId($params['customer_id'])
->setCredential($params['password']);
return $authAdapter;
}
I use two columns for my Zend_Auth_Adapter_DbTable, and it looks like this:
$authAdapter = new Zend_Auth_Adapter_DbTable(
Zend_Registry::get('database'),
"user",
"username",
"password_hash", // column 1
"MD5( CONCAT(?,password_salt) )" // column 2
);
When authenticating, the SQL ends up looking like this:
SELECT `user`.*,
(CASE WHEN `password_hash` = MD5( CONCAT('password entered',password_salt) )
THEN 1 ELSE 0 END) AS `zend_auth_credential_match`
FROM `user` WHERE (`username` = 'username entered')
So, that checks an additional column, password_salt, and for me it works well. I don't know if that helps you, as I don't know what you're trying to do.
The included Auth Adapater Zend_Auth_Adapter_DbTable does not allow you to check an additional column. You could extend the Zend_Auth_Adapter_DbTable class and add another column. You will have to add a member variable for the value of new field $_otherFieldValue and a public function setMemberField($value). Finally, you would have to override:
protected function _authenticateCreateSelect()
Hope that helps.
I know this question may be already solved but you can also solve this without needing to extend the class, use the getDbSelect() method and add the where clause to it:
$authAdapter = new Zend_Auth_Adapter_DbTable(Model_Users::getDefaultAdapter());
$authAdapter->setTableName('users');
$authAdapter->setIdentityColumn('email');
$authAdapter->setCredentialColumn('password')->setCredentialTreatment('md5(?)');
$authAdapter->setIdentity($email);
$authAdapter->setCredential($password);
$authAdapter->getDbSelect()->where('active = 1');