I created add and view function on my module already. but I have problem to do edit and delete function.
here is my add and view function

//submit function
function schedule_form_submit($form, &$form_state) {
$subm = db_insert('schedule')
->fields(array(
'day' => $form_state['values']['day'],
'time' => $form_state['values']['time'],
'content' => $form_state['values']['content'],
))
->execute();

drupal_set_message(t('Schedule added'));
}

//view function
function schedule_submissions() {
$result = db_query("SELECT * FROM {schedule} ORDER BY day ASC, time ASC");

$header = array(t('Day'), t('Time'), t('Programs'));
$rows = array();

foreach($result as $rl) {
$rows[] = array (
$rl->day,
$rl->time,
$rl->content,
);
}
return theme('table', array('header' => $header, 'rows' => $rows));
}

but how do I add "Edit/Delete" links to each record on view page?
and do we have Edit and Delete function template?

Thanks

Comments

nevets’s picture

Why not use a content type and views to create the table view.

Jaypan’s picture

Here is a stripped down example:

mymodule.install:

function mymodule_schema()
{
  $schema['my_table'] = array
  (
    'description' => t('An example table'),
    'fields' => array
    (
      'rid' => array
      (
        'description' => t('The unique Row ID'),
        'type' => 'serial',
      ),
      'my_field' => array
      (
        'description' => t('A field to hold a value'),
        'type' => 'text',
      ),
    ),
    'primary key' => array('rid'),
  );
  return $schema;
}

mymodule.module:

function mymodule_menu()
{
  $menu['mycontent/add'] = array
  (
    'title' => 'Add new item',
    'page callback' => 'mymodule_add_item_page',
    'access callback' => TRUE,
  );
  $menu['mycontent/%mycontent'] = array
  (
    'title' => 'View item',
    'page callback' => 'mymodule_view_item_page',
    'page arguments' => array(1),
    'access callback' => TRUE,
  );
  $menu['mycontent/%mycontent/view'] = array
  (
    'title' => 'View',
    'access callback' => TRUE,
    'type' => MENU_DEFAULT_LOCAL_TASK,
	'weight' => -2,
  );
  $menu['mycontent/%mycontent/edit'] = array
  (
    'title' => 'Edit',
    'page callback' => 'drupal_get_form',
    'page arguments' => array('mymodule_item_form', 1),
    'access arguments' => array('edit mymodule items'),
    'type' => MENU_LOCAL_TASK,
	'weight' => -1,
  );
  $menu['mycontent/%mycontent/delete'] = array
  (
    'title' => 'Delete',
    'page callback' => 'drupal_get_form',
    'page arguments' => array('mymodule_item_delete_form', 1),
    'access arguments' => array('delete mymodule items'),
    'type' => MENU_LOCAL_TASK,
  );
  return $menu;
}

function mycontent_load($rid)
{
  return db_query('SELECT rid, my_field FROM {my_table} WHERE rid = :rid', array(':rid' => $rid))->fetchObject();
}

function mymodule_add_item_page()
{
  $item = new StdClass;
  $item->rid = FALSE;
  $item->my_field = '';

  return drupal_get_form('mymodule_item_form', $item);
}

function mymodule_item_form($form, &$form_state, $item)
{
  $form['#item'] = $item;
  $form['my_field'] = array
  (
    '#title' => t('Enter a value'),
    '#type' => 'textfield',
    '#default_value' => $item->my_field,
  );
  $form['submit'] = array
  (
    '#type' => 'submit',
    '#value' => t('Save'),
  );
  return $form;
}

function mymodule_item_form_submit($form, &$form_state)
{
  $item = $form['#item'];
  if(isset($item->rid) && $item->rid)
  {
    db_update('my_table')
    ->fields(array('my_field' => $form_state['values']['my_field']))
    ->condition('rid', $item->rid)
    ->execute();
    $form_state['redirect'] = 'mycontent/' . $item->rid;
  }
  else
  {
    $rid = db_insert('my_table')
    ->fields(array('my_field' => $form_state['values']['my_field']))
    ->execute();
    $form_state['redirect'] = 'mycontent/' . $rid;
  }
}

function mymodule_view_item_page($item)
{
  $page['mymodule_item_page'] = array
  (
    '#markup' => t('The value of the item is <em>@value</em>', array('@value' => $item->my_field)),
    '#prefix' => '<p>',
    '#suffix' => '</p>',
  );

  return $page;
}

function mymodule_item_delete_form($form, &$form_state, $item)
{
  $form['#item'] = $item;
  // Note confirm_form() can be used here, but I prefer to use my own for styling purposes
  $form['header'] = array
  (
    '#markup' => t('Are you sure you wish to delete the item with a field value of <em>@value</em>?', array('@value' => $item->my_field)),
    '#prefix' => '<h2>',
    '#suffix' => '</h2>',
  );
  $form['warning'] = array
  (
    '#markup' => t('Warning, this action cannot be undone'),
	'#prefix' => '<p>',
	'#suffix' => '</p>',
  );
  $form['delete_button'] = array
  (
    '#type' => 'submit',
    '#value' => t('Delete item'),
  );
  return $form;
}

function mymodule_item_delete_form_submit($form, &$form_state)
{
  if($form_state['values']['op'] == $form_state['values']['delete_button'])
  {
    $item = $form['#item'];
    db_delete('my_table')
    ->condition('rid', $item->rid)
    ->execute();
  }
  $form_state['redirect'] = '<front>';
  drupal_set_message(t('The item has been deleted'));
}

This gives the whole scope of having your own item with a database table, creating a page for it, 'view', 'edit' and 'delete' tabs, and tying the functionality into those tabs.

Commenting it is a little more than I want to do, so if you have any specific questions, please feel free to ask and I (or someone else) will be happy to answer.

drupal_newbie123’s picture

thanks so much for your help, but I don't get how you pass item's id to edit and delete page. user should has right to choose which record they wanna delete/edit. so how you do this part?

thanks

Jaypan’s picture

There are three pieces of code that relate to this. I'll show you how an item is passed to the edit page, and delete page works in the same manner. First, look at my hook_menu() definition for the edit page (I've stripped it down to the relevant lines):

$menu['mycontent/%mycontent/edit'] = array
  (
    // ...
    'page arguments' => array('mymodule_item_form', 1),
    // ...
  );

First is the menu path. You'll notice the %mycontent element of the path. This means that any value can be used in this element, and that value will be passed to the function mycontent_load() (add _load() to the part after the % sign). If I had written it like this:

$menu['mycontent/%/edit'] = array

Then the value in that element of the path would be directly passed to the edit page, without going through the function mycontent_load(). However, my form function is set up to require that an $item is passed to the form function, so I need to load that $item first. This is what happens in mycontent_load().

The next line is here:

 'page arguments' => array('mymodule_item_form', 1),

I am passing two arguments to the callback function drupal_get_form(). The first argument is the name of the function that defines the form, in this case my_module_item_form(). The second argument is an integer, 1. When passing an integer, that index of the menu path is passed to the callback function. Indexes start at zero, so with the above path our indexes are:

0 = mycontent
1 = %mycontent
2 = edit

We want to pass the value from the wildcard, which is why 1 is passed.

The thing to look at is mycontent_load():

function mycontent_load($rid)
{
  return db_query('SELECT rid, my_field FROM {my_table} WHERE rid = :rid', array(':rid' => $rid))->fetchObject();
}

You can see this function receives one argument, $rid. This is the value that was entered into the path. So if someone accesses the path mycontent/3/edit, then the value of $rid would be 3. This value is then used in the db query to load the $item with an $rid of 3.

Note that if a value is not returned here, or if the returned value is false, then the form is never called and the user receives an 'access denied' page (or 'page not found'? I can't remember).

Now that we have generated an $item, it is passed to the form definition here:

function mymodule_item_form($form, &$form_state, $item)

All form definitions (in D7) receive $form, and $form_state. If you pass additional arguments to the function when calling drupal_get_form(), they are also passed to the form definition. Since I passed a loaded $item from the menu path, via mycontent_load(), the form definition receives $item. This is the item to be edited, and you can see I've used it to pass the default value (the value to be edited) to the form element here:

$form['my_field'] = array
  (
    '#title' => t('Enter a value'),
    '#type' => 'textfield',
    '#default_value' => $item->my_field,
  );

If there were more form elements, I would also populate them from the $item object.

This requirement for the $item is why for the item add page, I created an empty $item, then passed it to the form definition here:

function mymodule_add_item_page()
{
  $item = new StdClass;
  $item->rid = FALSE;
  $item->my_field = '';

  return drupal_get_form('mymodule_item_form', $item);
}

You can see I've called drupal_get_form(), passing it the form definition name, mymodule_item_form(), and the empty $item I created.

So to review:

1) The item ID is passed in the URL. Ex: mycontent/5/edit
2) This item ID is used to load an $item in mycontent_load()
3) The $item created in step 2 is passed to the form definition, as is the $item that will be edited.

drupal_newbie123’s picture

Hi Jaypan
Thanks so much for your help. I was out of town for while, so sorry not to reply you back in time. Thanks very much again

simnav’s picture

mycontent/%mycontent in this menu what is %mycontent?
And mymodule_item_form($form, &$form_state, $item) How data is fetched in $item
I am totally confuse i need to make a edit function ad trying to understand your concept but it is complex for me.
Please respond to clear my doubt

WorldFallz’s picture

one other method to consider is using entities which is sort of the best of both worlds-- you get a custom item that's not a node, but get lots of good stuff (like views) automatically.

Jaypan’s picture

Yeah, the example I've given here is for something really stripped down, that needs no interaction with anything else on the site.

Now that said, the code I've given here is essentially the same skeleton I use for my own custom entities. This code can be extended to provide custom entities, by tying into the Entity API with hook_entity_info() and the other relevant hooks.

WorldFallz’s picture

On an only tangentially related note, since you work with custom entities a lot, are they any faster in general? I'm working on upgrading a site from d6 to d7 and I'm trying to decide if it's worthwhile to redo my content types as custom entities. If I do them as entities, I can lose the field api fields and simply add them directly to the entity. I'm thinking that between shedding field api and the overhead typically incurred with node_load and node_save it could be a performance boost, but I have no data to back that up.

nevets’s picture

I would guess it would be a very small savings since the node functions are basically wrappers around the entity functions.

Jaypan’s picture

I agree with nevets - the node API adds a fair bit of overhead with all the options associated with node entities. Unless you need everything that comes with nodes - authoring info, comments, revision etc, the entity api is somewhat overkill. So you will see small savings.

I work purely with my own custom entities now, even for comments. I create my own comment entity type, and attach it with my own API. The Drupal comment API is near impossible to tie to other entity types unfortunately.

This all said, I actually created my own module that lets me create entity types through the admin interface, same as one creates content types with nodes. I just added an extra layer, so first the entity type is created, then the bundle type. I've thought about putting it up on DO, but I just don't have the time to maintain it, and since my client sites are all built around it, if I bring in other maintainers, and it goes off in a direction I'm not prepared for, it could cause me a lot of troubles! So we just use it in-house.

Chevap’s picture

I have the something simmilar to what you posted Jaypan. In mine users input data into a form (picture a online manual for a specific machine). Now the data is stored and then it is displayed with a view/edit/delete tab(simmilar to yours) so everyone can see it. Next to login link i have My submissions link where you have in a table view everything you posted.
My problem is next i need to make it so that a user who created the input can edit and delete his input (Like edit own article, delete own article permission). I save a global $user to table so i have the uid of the user. Now i am using if($curent_user == $saved_user) {delete code}, same for edit.
Now the user who did not create the input can't view the edit form od the delete form but they see delete and edit in tab. You know they have View|Edit|Delete . They can see View but under Edit and delete it is just blank space. Any tips ???

Jaypan’s picture

You want to set up an access callback for this. In my original code, I had this:

  $menu['mycontent/%mycontent/edit'] = array
  (
    'title' => 'Edit',
    'page callback' => 'drupal_get_form',
    'page arguments' => array('mymodule_item_form', 1),
    'access arguments' => array('edit mymodule items'),
    'type' => MENU_LOCAL_TASK,
    'weight' => -1,
  );

I'm going to add an access callback, and change the access arguments:

  $menu['mycontent/%mycontent/edit'] = array
  (
    'title' => 'Edit',
    'page callback' => 'drupal_get_form',
    'page arguments' => array('mymodule_item_form', 1),
    'access callback' => 'mymodule_access_callback',
    'access arguments' => array('edit', 1),
    'type' => MENU_LOCAL_TASK,
    'weight' => -1,
  );

Now we need to define the access callback:

function mymodule_access_callback($op, $item)
{
  global $user;

  if($op == 'edit')
 {
    return $user->uid == $item->uid;
  }
}

The 'return' of this function will be either true or false, which will determine whether or not to render the tab.

gibbo1715’s picture

Hi Jaypan,

Building entity types is what I'm looking into next, are you willing to share (or sell) this module that creates entity types from the admin with me so I can learn from it?

Thanks

Jaypan’s picture

I'd love to sell you mine, but you can get one for free on this site! Check out the Entity Construction Kit module.

er.pushpinderrana’s picture

Hi Jaypan,

I have seen your example code that gives a very clear way how to manage custom schema using custom module. I want to implement this example code by Entity API because I need to use this schema inside views also.

code I've given here is essentially the same skeleton I use for my own custom entities. This code can be extended to provide custom entities, by tying into the Entity API with hook_entity_info() and the other relevant hooks.

Can you please share any example or suggestion regarding this, it would help me in correct implementation.

Thanks

Pushpinder Rana #pushpinderdrupal
Acquia Certified Drupal Expert

Jaypan’s picture

Hello Pushpinder

I'm sorry, I don't have an example with how to tie this into entities or the Entity API module. I'll be writing one in a month or so, but at the time I have nothing.

But, if you look at a tutorial on how to create your own custom entities, it will likely require some forms, and you can use the ideas I've shown in this thread to tie in with your custom Entity.

er.pushpinderrana’s picture

Thanks Jaypan!!!

I got one example from drupal forums and it is good for newcomers. entity_example

I will look into this and do correlate with your code.

Thanks

Pushpinder Rana #pushpinderdrupal
Acquia Certified Drupal Expert

Motlapele’s picture

Hi,

I have only started learning how to build Drupal custom modules a few weeks ago and am really a coding newbie. I would like to view, edit etc records in an external mssql database. I have built a module using hook_views_api to expose the tables to views and have successfully created views, but I am simply finding it impossible to edit records in the external database using example forms that i have come across.

Please help!!

gibbo1715’s picture

Hi Jaypan, Did you ever get around to doing this (example with how to tie this into entities or the Entity API module), I'm enjoying learning Drupal and very interested in this if you ever did get around to an example

Many Thanks

Jaypan’s picture

Hmm, I don't actually remember ever writing that comment, but I didn't ever write that tutorial, sorry!

gibbo1715’s picture

No problem, You have already helped me massively so thankyou